]> git.sesse.net Git - vlc/blob - modules/video_filter/distort.c
Do not provide a broken language option on Linux and similar OSes
[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. It is only used by " \
73     "\"gradient\" 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, using wave" );
209             p_vout->p_sys->i_mode = WAVE;
210         }
211     }
212     free( psz_method_tmp );
213
214     p_vout->p_sys->i_gradient_type =
215         config_GetInt( p_vout, "distort-gradient-type" );
216     p_vout->p_sys->b_cartoon =
217         config_GetInt( p_vout, "distort-cartoon" );
218
219     return VLC_SUCCESS;
220 }
221
222 /*****************************************************************************
223  * Init: initialize Distort video thread output method
224  *****************************************************************************/
225 static int Init( vout_thread_t *p_vout )
226 {
227     int i_index;
228     picture_t *p_pic;
229     video_format_t fmt = {0};
230
231     I_OUTPUTPICTURES = 0;
232
233     /* Initialize the output structure */
234     p_vout->output.i_chroma = p_vout->render.i_chroma;
235     p_vout->output.i_width  = p_vout->render.i_width;
236     p_vout->output.i_height = p_vout->render.i_height;
237     p_vout->output.i_aspect = p_vout->render.i_aspect;
238     p_vout->fmt_out = p_vout->fmt_in;
239     fmt = p_vout->fmt_out;
240
241     /* Try to open the real video output */
242     msg_Dbg( p_vout, "spawning the real video output" );
243
244     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
245
246     /* Everything failed */
247     if( p_vout->p_sys->p_vout == NULL )
248     {
249         msg_Err( p_vout, "cannot open vout, aborting" );
250         return VLC_EGENERIC;
251     }
252
253     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
254
255     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
256
257     ADD_PARENT_CALLBACKS( SendEventsToChild );
258
259     p_vout->p_sys->f_angle = 0.0;
260     p_vout->p_sys->last_date = 0;
261
262     p_vout->p_sys->p_image = NULL;
263
264     return VLC_SUCCESS;
265 }
266
267 /*****************************************************************************
268  * End: terminate Distort video thread output method
269  *****************************************************************************/
270 static void End( vout_thread_t *p_vout )
271 {
272     int i_index;
273
274     /* Free the fake output buffers we allocated */
275     for( i_index = I_OUTPUTPICTURES ; i_index ; )
276     {
277         i_index--;
278         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
279     }
280 }
281
282 /*****************************************************************************
283  * Destroy: destroy Distort video thread output method
284  *****************************************************************************
285  * Terminate an output method created by DistortCreateOutputMethod
286  *****************************************************************************/
287 static void Destroy( vlc_object_t *p_this )
288 {
289     vout_thread_t *p_vout = (vout_thread_t *)p_this;
290
291     if( p_vout->p_sys->p_vout )
292     {
293         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
294         vlc_object_detach( p_vout->p_sys->p_vout );
295         vout_Destroy( p_vout->p_sys->p_vout );
296     }
297
298     DEL_PARENT_CALLBACKS( SendEventsToChild );
299
300     if(p_vout->p_sys->p_pre_hough)
301         free(p_vout->p_sys->p_pre_hough);
302
303     if( p_vout->p_sys->p_image )
304         image_HandlerDelete( p_vout->p_sys->p_image );
305
306     free( p_vout->p_sys );
307 }
308
309 /*****************************************************************************
310  * Render: displays previously rendered output
311  *****************************************************************************
312  * This function send the currently rendered image to Distort image, waits
313  * until it is displayed and switch the two rendering buffers, preparing next
314  * frame.
315  *****************************************************************************/
316 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
317 {
318     picture_t *p_outpic;
319
320     /* This is a new frame. Get a structure from the video_output. */
321     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
322               == NULL )
323     {
324         if( p_vout->b_die || p_vout->b_error )
325         {
326             return;
327         }
328         msleep( VOUT_OUTMEM_SLEEP );
329     }
330
331     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
332
333     switch( p_vout->p_sys->i_mode )
334     {
335         case WAVE:
336             DistortWave( p_vout, p_pic, p_outpic );
337             break;
338
339         case RIPPLE:
340             DistortRipple( p_vout, p_pic, p_outpic );
341             break;
342
343         case EDGE:
344             DistortEdge( p_vout, p_pic, p_outpic );
345             break;
346
347         case GRADIENT:
348             DistortGradient( p_vout, p_pic, p_outpic );
349             break;
350
351         case HOUGH:
352             DistortHough( p_vout, p_pic, p_outpic );
353             break;
354
355         case PSYCHEDELIC:
356             DistortPsychedelic( p_vout, p_pic, p_outpic );
357             break;
358
359         default:
360             break;
361     }
362
363     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
364 }
365
366 /*****************************************************************************
367  * DistortWave: draw a wave effect on the picture
368  *****************************************************************************/
369 static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
370                                                 picture_t *p_outpic )
371 {
372     int i_index;
373     double f_angle;
374     mtime_t new_date = mdate();
375
376     p_vout->p_sys->f_angle += (new_date - p_vout->p_sys->last_date) / 200000.0;
377     p_vout->p_sys->last_date = new_date;
378     f_angle = p_vout->p_sys->f_angle;
379
380     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
381     {
382         int i_line, i_num_lines, i_offset;
383         uint8_t black_pixel;
384         uint8_t *p_in, *p_out;
385
386         p_in = p_inpic->p[i_index].p_pixels;
387         p_out = p_outpic->p[i_index].p_pixels;
388
389         i_num_lines = p_inpic->p[i_index].i_visible_lines;
390
391         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
392
393         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
394         for( i_line = 0 ; i_line < i_num_lines ; i_line++ )
395         {
396             /* Calculate today's offset, don't go above 1/20th of the screen */
397             i_offset = (int)( (double)(p_inpic->p[i_index].i_visible_pitch)
398                          * sin( f_angle + 10.0 * (double)i_line
399                                                / (double)i_num_lines )
400                          / 20.0 );
401
402             if( i_offset )
403             {
404                 if( i_offset < 0 )
405                 {
406                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
407                              p_inpic->p[i_index].i_visible_pitch + i_offset );
408                     p_in += p_inpic->p[i_index].i_pitch;
409                     p_out += p_outpic->p[i_index].i_pitch;
410                     memset( p_out + i_offset, black_pixel, -i_offset );
411                 }
412                 else
413                 {
414                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
415                              p_inpic->p[i_index].i_visible_pitch - i_offset );
416                     memset( p_out, black_pixel, i_offset );
417                     p_in += p_inpic->p[i_index].i_pitch;
418                     p_out += p_outpic->p[i_index].i_pitch;
419                 }
420             }
421             else
422             {
423                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
424                                           p_inpic->p[i_index].i_visible_pitch );
425                 p_in += p_inpic->p[i_index].i_pitch;
426                 p_out += p_outpic->p[i_index].i_pitch;
427             }
428
429         }
430     }
431 }
432
433 /*****************************************************************************
434  * DistortRipple: draw a ripple effect at the bottom of the picture
435  *****************************************************************************/
436 static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
437                                                   picture_t *p_outpic )
438 {
439     int i_index;
440     double f_angle;
441     mtime_t new_date = mdate();
442
443     p_vout->p_sys->f_angle -= (p_vout->p_sys->last_date - new_date) / 100000.0;
444     p_vout->p_sys->last_date = new_date;
445     f_angle = p_vout->p_sys->f_angle;
446
447     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
448     {
449         int i_line, i_first_line, i_num_lines, i_offset;
450         uint8_t black_pixel;
451         uint8_t *p_in, *p_out;
452
453         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
454
455         i_num_lines = p_inpic->p[i_index].i_visible_lines;
456
457         i_first_line = i_num_lines * 4 / 5;
458
459         p_in = p_inpic->p[i_index].p_pixels;
460         p_out = p_outpic->p[i_index].p_pixels;
461
462         for( i_line = 0 ; i_line < i_first_line ; i_line++ )
463         {
464             p_vout->p_vlc->pf_memcpy( p_out, p_in,
465                                       p_inpic->p[i_index].i_visible_pitch );
466             p_in += p_inpic->p[i_index].i_pitch;
467             p_out += p_outpic->p[i_index].i_pitch;
468         }
469
470         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
471         for( i_line = i_first_line ; i_line < i_num_lines ; i_line++ )
472         {
473             /* Calculate today's offset, don't go above 1/20th of the screen */
474             i_offset = (int)( (double)(p_inpic->p[i_index].i_pitch)
475                          * sin( f_angle + 2.0 * (double)i_line
476                                               / (double)( 1 + i_line
477                                                             - i_first_line) )
478                          * (double)(i_line - i_first_line)
479                          / (double)i_num_lines
480                          / 8.0 );
481
482             if( i_offset )
483             {
484                 if( i_offset < 0 )
485                 {
486                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
487                              p_inpic->p[i_index].i_visible_pitch + i_offset );
488                     p_in -= p_inpic->p[i_index].i_pitch;
489                     p_out += p_outpic->p[i_index].i_pitch;
490                     memset( p_out + i_offset, black_pixel, -i_offset );
491                 }
492                 else
493                 {
494                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
495                              p_inpic->p[i_index].i_visible_pitch - i_offset );
496                     memset( p_out, black_pixel, i_offset );
497                     p_in -= p_inpic->p[i_index].i_pitch;
498                     p_out += p_outpic->p[i_index].i_pitch;
499                 }
500             }
501             else
502             {
503                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
504                                           p_inpic->p[i_index].i_visible_pitch );
505                 p_in -= p_inpic->p[i_index].i_pitch;
506                 p_out += p_outpic->p[i_index].i_pitch;
507             }
508
509         }
510     }
511 }
512
513 /*****************************************************************************
514  * Gaussian Convolution
515  *****************************************************************************
516  *    Gaussian convolution ( sigma == 1.4 )
517  *
518  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
519  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
520  *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
521  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
522  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
523  *****************************************************************************/
524 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
525 {
526     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
527     int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
528     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
529     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
530
531     int x,y;
532     for( y = 2; y < i_num_lines - 2; y++ )
533     {
534         for( x = 2; x < i_src_visible - 2; x++ )
535         {
536             p_smooth[y*i_src_visible+x] = (uint32_t)(
537               /* 2 rows up */
538                 ( p_inpix[(y-2)*i_src_pitch+x-2]<<1 )
539               + ( p_inpix[(y-2)*i_src_pitch+x-1]<<2 )
540               + ( p_inpix[(y-2)*i_src_pitch+x]<<2 )
541               + ( p_inpix[(y-2)*i_src_pitch+x+1]<<2 )
542               + ( p_inpix[(y-2)*i_src_pitch+x+2]<<1 )
543               /* 1 row up */
544               + ( p_inpix[(y-1)*i_src_pitch+x-2]<<2 )
545               + ( p_inpix[(y-1)*i_src_pitch+x-1]<<3 )
546               + ( p_inpix[(y-1)*i_src_pitch+x]*12 )
547               + ( p_inpix[(y-1)*i_src_pitch+x+1]<<3 )
548               + ( p_inpix[(y-1)*i_src_pitch+x+2]<<2 )
549               /* */
550               + ( p_inpix[y*i_src_pitch+x-2]<<2 )
551               + ( p_inpix[y*i_src_pitch+x-1]*12 )
552               + ( p_inpix[y*i_src_pitch+x]<<4 )
553               + ( p_inpix[y*i_src_pitch+x+1]*12 )
554               + ( p_inpix[y*i_src_pitch+x+2]<<2 )
555               /* 1 row down */
556               + ( p_inpix[(y+1)*i_src_pitch+x-2]<<2 )
557               + ( p_inpix[(y+1)*i_src_pitch+x-1]<<3 )
558               + ( p_inpix[(y+1)*i_src_pitch+x]*12 )
559               + ( p_inpix[(y+1)*i_src_pitch+x+1]<<3 )
560               + ( p_inpix[(y+1)*i_src_pitch+x+2]<<2 )
561               /* 2 rows down */
562               + ( p_inpix[(y+2)*i_src_pitch+x-2]<<1 )
563               + ( p_inpix[(y+2)*i_src_pitch+x-1]<<2 )
564               + ( p_inpix[(y+2)*i_src_pitch+x]<<2 )
565               + ( p_inpix[(y+2)*i_src_pitch+x+1]<<2 )
566               + ( p_inpix[(y+2)*i_src_pitch+x+2]<<1 )
567               ) >> 7 /* 115 */;
568         }
569     }
570 }
571
572 /*****************************************************************************
573  * DistortGradient: Sobel
574  *****************************************************************************/
575 static void DistortGradient( vout_thread_t *p_vout, picture_t *p_inpic,
576                                                   picture_t *p_outpic )
577 {
578     int x, y;
579     int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
580     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
581     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
582     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
583
584     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
585     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
586
587     uint32_t *p_smooth = (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
588
589     if( !p_smooth ) return;
590
591     if( p_vout->p_sys->b_cartoon )
592     {
593         p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
594             p_inpic->p[U_PLANE].p_pixels,
595             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
596         p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
597             p_inpic->p[V_PLANE].p_pixels,
598             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
599     }
600     else
601     {
602         p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
603             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
604         p_vout->p_vlc->pf_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
605             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
606     }
607
608     GaussianConvolution( p_inpic, p_smooth );
609
610     /* Sobel gradient
611
612      | -1 0 1 |     |  1  2  1 |
613      | -2 0 2 | and |  0  0  0 |
614      | -1 0 1 |     | -1 -2 -1 | */
615
616     for( y = 1; y < i_num_lines - 1; y++ )
617     {
618         for( x = 1; x < i_src_visible - 1; x++ )
619         {
620             uint32_t a =
621             (
622               abs(
623                 ( ( p_smooth[(y-1)*i_src_visible+x]
624                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
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               )
630             +
631               abs(
632                 ( ( p_smooth[y*i_src_visible+x-1]
633                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
634                + ( p_smooth[(y-1)*i_src_visible+x-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               )
639             );
640             if( p_vout->p_sys->i_gradient_type )
641             {
642                 if( p_vout->p_sys->b_cartoon )
643                 {
644                     if( a > 60 )
645                     {
646                         p_outpix[y*i_dst_pitch+x] = 0x00;
647                     }
648                     else
649                     {
650                         if( p_smooth[y*i_src_visible+x] > 0xa0 )
651                             p_outpix[y*i_dst_pitch+x] =
652                                 0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
653                         else if( p_smooth[y*i_src_visible+x] > 0x70 )
654                             p_outpix[y*i_dst_pitch+x] =
655                                 0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
656                         else if( p_smooth[y*i_src_visible+x] > 0x28 )
657                             p_outpix[y*i_dst_pitch+x] =
658                                 0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
659                         else
660                             p_outpix[y*i_dst_pitch+x] =
661                                 0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
662                     }
663                 }
664                 else
665                 {
666                     if( a>>8 )
667                         p_outpix[y*i_dst_pitch+x] = 255;
668                     else
669                         p_outpix[y*i_dst_pitch+x] = (uint8_t)a;
670                 }
671             }
672             else
673             {
674                 if( a>>8 )
675                     p_outpix[y*i_dst_pitch+x] = 0;
676                 else
677                     p_outpix[y*i_dst_pitch+x] = (uint8_t)(255 - a);
678             }
679         }
680     }
681
682     if( p_smooth ) free( p_smooth );
683 }
684
685 /*****************************************************************************
686  * DistortEdge: Canny edge detection algorithm
687  *****************************************************************************
688  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
689  * (well ... my implementation isn't really the canny algorithm ... but some
690  * ideas are the same)
691  *****************************************************************************/
692 /* angle : | */
693 #define THETA_Y 0
694 /* angle : - */
695 #define THETA_X 1
696 /* angle : / */
697 #define THETA_P 2
698 /* angle : \ */
699 #define THETA_M 3
700 static void DistortEdge( vout_thread_t *p_vout, picture_t *p_inpic,
701                                                   picture_t *p_outpic )
702 {
703     int x, y;
704
705     int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
706     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
707     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
708     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
709
710     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
711     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
712
713     uint32_t *p_smooth = malloc( i_num_lines*i_src_visible * sizeof(uint32_t) );
714     uint32_t *p_grad = malloc( i_num_lines*i_src_visible *sizeof(uint32_t) );
715     uint8_t *p_theta = malloc( i_num_lines*i_src_visible *sizeof(uint8_t) );
716
717     if( !p_smooth || !p_grad || !p_theta ) return;
718
719     if( p_vout->p_sys->b_cartoon )
720     {
721         p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
722             p_inpic->p[U_PLANE].p_pixels,
723             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
724         p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
725             p_inpic->p[V_PLANE].p_pixels,
726             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
727     }
728     else
729     {
730         p_vout->p_vlc->pf_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
731               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
732         p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
733             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
734         memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
735             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
736     }
737
738     GaussianConvolution( p_inpic, p_smooth );
739
740     /* Sobel gradient
741
742      | -1 0 1 |     |  1  2  1 |
743      | -2 0 2 | and |  0  0  0 |
744      | -1 0 1 |     | -1 -2 -1 | */
745
746     for( y = 1; y < i_num_lines - 1; y++ )
747     {
748         for( x = 1; x < i_src_visible - 1; x++ )
749         {
750
751             int gradx =
752                 ( ( p_smooth[(y-1)*i_src_visible+x]
753                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
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             int grady =
759                 ( ( p_smooth[y*i_src_visible+x-1]
760                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
761                + ( p_smooth[(y-1)*i_src_visible+x-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
766             p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
767
768             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
769              * tan( 26,565051177 ) = 0.5
770              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
771              * tan( 63,434948823 ) 2 */
772             if( (grady<<1) > gradx )
773                 p_theta[y*i_src_visible+x] = THETA_P;
774             else if( (grady<<1) < -gradx )
775                 p_theta[y*i_src_visible+x] = THETA_M;
776             else if( !gradx || abs(grady) > abs(gradx)<<1 )
777                 p_theta[y*i_src_visible+x] = THETA_Y;
778             else
779                 p_theta[y*i_src_visible+x] = THETA_X;
780         }
781     }
782
783     /* edge computing */
784     for( y = 1; y < i_num_lines - 1; y++ )
785     {
786         for( x = 1; x < i_src_visible - 1; x++ )
787         {
788             if( p_grad[y*i_src_visible+x] > 40 )
789             {
790                 switch( p_theta[y*i_src_visible+x] )
791                 {
792                     case THETA_Y:
793                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
794                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
795                         {
796                             p_outpix[y*i_dst_pitch+x] = 0;
797                         } else goto colorize;
798                         break;
799                     case THETA_P:
800                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
801                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
802                         {
803                             p_outpix[y*i_dst_pitch+x] = 0;
804                         } else goto colorize;
805                         break;
806                     case THETA_M:
807                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
808                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
809                         {
810                             p_outpix[y*i_dst_pitch+x] = 0;
811                         } else goto colorize;
812                         break;
813                     case THETA_X:
814                         if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
815                             && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
816                         {
817                             p_outpix[y*i_dst_pitch+x] = 0;
818                         } else goto colorize;
819                         break;
820                 }
821             }
822             else
823             {
824                 colorize:
825                 if( p_vout->p_sys->b_cartoon )
826                 {
827                     if( p_smooth[y*i_src_visible+x] > 0xa0 )
828                         p_outpix[y*i_dst_pitch+x] = (uint8_t)
829                             0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
830                     else if( p_smooth[y*i_src_visible+x] > 0x70 )
831                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
832                             0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
833                     else if( p_smooth[y*i_src_visible+x] > 0x28 )
834                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
835                             0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
836                     else
837                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
838                             0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
839                 }
840             }
841         }
842     }
843     if( p_smooth ) free( p_smooth );
844     if( p_grad ) free( p_grad );
845     if( p_theta) free( p_theta );
846 }
847
848 /*****************************************************************************
849  * DistortHough
850  *****************************************************************************/
851 #define p_pre_hough p_vout->p_sys->p_pre_hough
852 static void DistortHough( vout_thread_t *p_vout, picture_t *p_inpic,
853                                                   picture_t *p_outpic )
854 {
855     int x, y, i;
856     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
857     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
858     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
859
860     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
861
862     int i_diag = sqrt( i_num_lines * i_num_lines +
863                         i_src_visible * i_src_visible);
864     int i_max, i_phi_max, i_rho, i_rho_max;
865     int i_nb_steps = 90;
866     double d_step = M_PI / i_nb_steps;
867     double d_sin;
868     double d_cos;
869     uint32_t *p_smooth;
870     int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
871     if( ! p_hough ) return;
872     p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
873     if( !p_smooth ) return;
874
875     if( ! p_pre_hough )
876     {
877         msg_Dbg(p_vout, "Starting precalculation");
878         p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
879         if( ! p_pre_hough ) return;
880         for( i = 0 ; i < i_nb_steps ; i++)
881         {
882             d_sin = sin(d_step * i);
883             d_cos = cos(d_step * i);
884             for( y = 0 ; y < i_num_lines ; y++ )
885                 for( x = 0 ; x < i_src_visible ; x++ )
886                 {
887                     p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
888                         ceil(x*d_sin + y*d_cos);
889                 }
890         }
891         msg_Dbg(p_vout, "Precalculation done");
892     }
893
894     memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
895
896     p_vout->p_vlc->pf_memcpy(
897         p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
898         p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
899     p_vout->p_vlc->pf_memcpy(
900         p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
901         p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
902     p_vout->p_vlc->pf_memcpy(
903         p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
904         p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
905
906     GaussianConvolution( p_inpic, p_smooth );
907
908     /* Sobel gradient
909
910      | -1 0 1 |     |  1  2  1 |
911      | -2 0 2 | and |  0  0  0 |
912      | -1 0 1 |     | -1 -2 -1 | */
913
914     i_max = 0;
915     i_rho_max = 0;
916     i_phi_max = 0;
917     for( y = 4; y < i_num_lines - 4; y++ )
918     {
919         for( x = 4; x < i_src_visible - 4; x++ )
920         {
921             uint32_t a =
922             (
923               abs(
924                 ( ( p_smooth[(y-1)*i_src_visible+x]
925                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
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               )
931             +
932               abs(
933                 ( ( p_smooth[y*i_src_visible+x-1]
934                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
935                + ( p_smooth[(y-1)*i_src_visible+x-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               )
940             );
941             if( a>>8 )
942             {
943                 for( i = 0 ; i < i_nb_steps ; i ++ )
944                 {
945                     i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
946                     if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
947                     {
948                         i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
949                         i_rho_max = i_rho;
950                         i_phi_max = i;
951                     }
952                 }
953             }
954         }
955     }
956
957     d_sin = sin(i_phi_max*d_step);
958     d_cos = cos(i_phi_max*d_step);
959     if( d_cos != 0 )
960     {
961         for( x = 0 ; x < i_src_visible ; x++ )
962         {
963             y = (i_rho_max - x * d_sin) / d_cos;
964             if( y >= 0 && y < i_num_lines )
965                 p_outpix[y*i_dst_pitch+x] = 255;
966         }
967     }
968
969     if( p_hough ) free( p_hough );
970     if( p_smooth ) free( p_smooth );
971 }
972 #undef p_pre_hough
973
974 /*****************************************************************************
975  * DistortPsychedelic
976  *****************************************************************************/
977 static void DistortPsychedelic( vout_thread_t *p_vout, picture_t *p_inpic,
978                                                   picture_t *p_outpic )
979 {
980     unsigned int w, h;
981     int x,y;
982     uint8_t u,v;
983
984     video_format_t fmt_out = {0};
985     picture_t *p_converted;
986
987     if( !p_vout->p_sys->p_image )
988         p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
989
990     /* chrominance */
991     u = p_vout->p_sys->u;
992     v = p_vout->p_sys->v;
993     for( y = 0; y<p_outpic->p[U_PLANE].i_lines; y++)
994     {
995         memset( p_outpic->p[U_PLANE].p_pixels+y*p_outpic->p[U_PLANE].i_pitch,
996                 u, p_outpic->p[U_PLANE].i_pitch );
997         memset( p_outpic->p[V_PLANE].p_pixels+y*p_outpic->p[V_PLANE].i_pitch,
998                 v, p_outpic->p[V_PLANE].i_pitch );
999         if( v == 0 && u != 0 )
1000             u --;
1001         else if( u == 0xff )
1002             v --;
1003         else if( v == 0xff )
1004             u ++;
1005         else if( u == 0 )
1006             v ++;
1007     }
1008
1009     /* luminance */
1010     p_vout->p_vlc->pf_memcpy(
1011                 p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
1012                 p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
1013
1014
1015     /* image visualization */
1016     fmt_out = p_vout->fmt_out;
1017     fmt_out.i_width = p_vout->render.i_width*p_vout->p_sys->scale/150;
1018     fmt_out.i_height = p_vout->render.i_height*p_vout->p_sys->scale/150;
1019     p_converted = image_Convert( p_vout->p_sys->p_image, p_inpic,
1020                                  &(p_inpic->format), &fmt_out );
1021
1022 #define copyimage( plane, b ) \
1023     for( y=0; y<p_converted->p[plane].i_visible_lines; y++) { \
1024     for( x=0; x<p_converted->p[plane].i_visible_pitch; x++) { \
1025         int nx, ny; \
1026         if( p_vout->p_sys->yinc == 1 ) \
1027             ny= y; \
1028         else \
1029             ny = p_converted->p[plane].i_visible_lines-y; \
1030         if( p_vout->p_sys->xinc == 1 ) \
1031             nx = x; \
1032         else \
1033             nx = p_converted->p[plane].i_visible_pitch-x; \
1034         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]; \
1035     } }
1036     copyimage( Y_PLANE, 2 );
1037     copyimage( U_PLANE, 1 );
1038     copyimage( V_PLANE, 1 );
1039 #undef copyimage
1040
1041     p_converted->pf_release( p_converted );
1042
1043     p_vout->p_sys->x += p_vout->p_sys->xinc;
1044     p_vout->p_sys->y += p_vout->p_sys->yinc;
1045
1046     p_vout->p_sys->scale += p_vout->p_sys->scaleinc;
1047     if( p_vout->p_sys->scale >= 50 ) p_vout->p_sys->scaleinc = -1;
1048     if( p_vout->p_sys->scale <= 1 ) p_vout->p_sys->scaleinc = 1;
1049
1050     w = p_vout->render.i_width*p_vout->p_sys->scale/150;
1051     h = p_vout->render.i_height*p_vout->p_sys->scale/150;
1052     if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
1053         p_vout->p_sys->xinc = -1;
1054     if( p_vout->p_sys->x <= 0 )
1055         p_vout->p_sys->xinc = 1;
1056
1057     if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
1058         p_vout->p_sys->x = (p_vout->render.i_width-w)/2;
1059     if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
1060         p_vout->p_sys->y = (p_vout->render.i_height-h)/2;
1061
1062     if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
1063         p_vout->p_sys->yinc = -1;
1064     if( p_vout->p_sys->y <= 0 )
1065         p_vout->p_sys->yinc = 1;
1066
1067     for( y = 0; y< 16; y++ )
1068     {
1069         if( p_vout->p_sys->v == 0 && p_vout->p_sys->u != 0 )
1070             p_vout->p_sys->u -= 1;
1071         else if( p_vout->p_sys->u == 0xff )
1072             p_vout->p_sys->v -= 1;
1073         else if( p_vout->p_sys->v == 0xff )
1074             p_vout->p_sys->u += 1;
1075         else if( p_vout->p_sys->u == 0 )
1076             p_vout->p_sys->v += 1;
1077     }
1078 }
1079
1080
1081 /*****************************************************************************
1082  * SendEvents: forward mouse and keyboard events to the parent p_vout
1083  *****************************************************************************/
1084 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
1085                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1086 {
1087     var_Set( (vlc_object_t *)p_data, psz_var, newval );
1088
1089     return VLC_SUCCESS;
1090 }
1091
1092 /*****************************************************************************
1093  * SendEventsToChild: forward events to the child/children vout
1094  *****************************************************************************/
1095 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
1096                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1097 {
1098     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1099     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
1100     return VLC_SUCCESS;
1101 }