]> git.sesse.net Git - vlc/blob - modules/video_filter/ball.c
Use var_InheritString for --decklink-video-connection.
[vlc] / modules / video_filter / ball.c
1 /*****************************************************************************
2  * ball.c : Augmented reality ball video filter module
3  *****************************************************************************
4  * Copyright (C) 2000-2009 the VideoLAN team
5  *
6  * Author: Adrien Maglo <magsoft@videolan.org>
7  *
8  * The Canny edge detection algorithm comes from gradient.c which was
9  * writen by:
10  *         Samuel Hocevar <sam@zoy.org>
11  *         Antoine Cellerier <dionoea -at- videolan -dot- org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <math.h> /* sin(), cos(), asin() */
38
39 #include <vlc_common.h>
40 #include <vlc_plugin.h>
41
42 #include "vlc_filter.h"
43 #include "filter_picture.h"
44 #include "vlc_image.h"
45
46 enum { RED, GREEN, BLUE, WHITE };
47
48 typedef struct
49 {
50     uint8_t comp1;
51     uint8_t comp2;
52     uint8_t comp3;
53 }COLOR;
54
55 static COLOR colorList[4];
56
57 #define COLORS_RGB \
58     colorList[RED].comp1 = 255; colorList[RED].comp2 = 0;        \
59                                 colorList[RED].comp3 = 0;        \
60     colorList[GREEN].comp1 = 0; colorList[GREEN].comp2 = 255;    \
61                                colorList[GREEN].comp3 = 0;       \
62     colorList[BLUE].comp1 = 0; colorList[BLUE].comp2 = 0;        \
63                                colorList[BLUE].comp3 = 255;      \
64     colorList[WHITE].comp1 = 255; colorList[WHITE].comp2 = 255;  \
65                                   colorList[WHITE].comp3 = 255;
66
67 #define COLORS_YUV \
68     colorList[RED].comp1 = 82; colorList[RED].comp2 = 240;        \
69                                 colorList[RED].comp3 = 90;        \
70     colorList[GREEN].comp1 = 145; colorList[GREEN].comp2 = 34;    \
71                                colorList[GREEN].comp3 = 54 ;      \
72     colorList[BLUE].comp1 = 41; colorList[BLUE].comp2 = 146;      \
73                                colorList[BLUE].comp3 = 240;       \
74     colorList[WHITE].comp1 = 255; colorList[WHITE].comp2 = 128;   \
75                                   colorList[WHITE].comp3 = 128;
76
77
78 /*****************************************************************************
79  * Local prototypes
80  *****************************************************************************/
81 static int  Create    ( vlc_object_t * );
82 static void Destroy   ( vlc_object_t * );
83
84 static picture_t *Filter( filter_t *, picture_t * );
85
86 static void drawBall( filter_sys_t *p_sys, picture_t *p_outpic );
87 static void drawPixelRGB24( filter_sys_t *p_sys, picture_t *p_outpic,
88                             uint8_t R, uint8_t G, uint8_t B,
89                             int x, int y, bool b_skip );
90 static void drawPixelI420( filter_sys_t *p_sys, picture_t *p_outpic,
91                            uint8_t Y, uint8_t U, uint8_t V,
92                            int x, int y, bool b_skip );
93 static void drawPixelPacked( filter_sys_t *p_sys, picture_t *p_outpic,
94                              uint8_t Y, uint8_t U, uint8_t V,
95                              int x, int y, bool b_skip );
96
97 static void FilterBall( filter_t *, picture_t *, picture_t * );
98 static int ballCallback( vlc_object_t *, char const *,
99                          vlc_value_t, vlc_value_t,
100                          void * );
101 static int getBallColor( vlc_object_t *p_this, char const *psz_newval );
102
103
104 /*****************************************************************************
105  * Module descriptor
106  *****************************************************************************/
107 #define BALL_COLOR_TEXT N_("Ball color")
108 #define BALL_COLOR_LONGTEXT N_("Ball color, one of \"red\", \"blue\" and \"green\".")
109
110 #define EDGE_VISIBLE_TEXT N_("Edge visible")
111 #define EDGE_VISIBLE_LONGTEXT N_("Set edge visibility.")
112
113 #define BALL_SPEED_TEXT N_("Ball speed")
114 #define BALL_SPEED_LONGTEXT N_("Set ball speed, the displacement value \
115                                 in number of pixels by frame.")
116
117 #define BALL_SIZE_TEXT N_("Ball size")
118 #define BALL_SIZE_LONGTEXT N_("Set ball size giving its radius in number \
119                                 of pixels")
120
121 #define GRAD_THRESH_TEXT N_("Gradient threshold")
122 #define GRAD_THRESH_LONGTEXT N_("Set gradient threshold for edge computation.")
123
124 #define BALL_HELP N_("Augmented reality ball game")
125
126 #define FILTER_PREFIX "ball-"
127
128 static const char *const mode_list[] = { "red", "green", "blue", "white" };
129 static const char *const mode_list_text[] = { N_("Red"), N_("Green"),
130                                               N_("Blue"), N_("White") };
131
132 vlc_module_begin ()
133     set_description( N_("Ball video filter") )
134     set_shortname( N_( "Ball" ))
135     set_help(BALL_HELP)
136     set_capability( "video filter2", 0 )
137     set_category( CAT_VIDEO )
138     set_subcategory( SUBCAT_VIDEO_VFILTER )
139
140     add_string( FILTER_PREFIX "color", "ball-color", NULL,
141                 BALL_COLOR_TEXT, BALL_COLOR_LONGTEXT, false )
142     change_string_list( mode_list, mode_list_text, 0 )
143
144     add_integer_with_range( FILTER_PREFIX "speed", 4, 1, 15, NULL,
145                             BALL_SPEED_TEXT, BALL_SPEED_LONGTEXT, false )
146
147     add_integer_with_range( FILTER_PREFIX "size", 10, 5, 30, NULL,
148                             BALL_SIZE_TEXT, BALL_SIZE_LONGTEXT, false )
149
150     add_integer_with_range( FILTER_PREFIX "gradient-threshold", 40, 1, 200, NULL,
151                             GRAD_THRESH_TEXT, GRAD_THRESH_LONGTEXT, false )
152
153     add_bool( FILTER_PREFIX "edge-visible", true, NULL,
154               EDGE_VISIBLE_TEXT, EDGE_VISIBLE_LONGTEXT, true )
155
156     add_shortcut( "ball" )
157     set_callbacks( Create, Destroy )
158 vlc_module_end ()
159
160 static const char *const ppsz_filter_options[] = {
161     "ball-color", "ball-speed", "ball-size",
162     "gradient-threshold", "edge-visible", NULL
163 };
164
165
166 /*****************************************************************************
167 * filter_sys_t: Distort video output method descriptor
168 *****************************************************************************
169 * This structure is part of the video output thread descriptor.
170 * It describes the Distort specific properties of an output thread.
171  *****************************************************************************/
172 struct filter_sys_t
173 {
174     vlc_mutex_t lock;
175
176     int ballColor;
177
178     image_handler_t *p_image;
179
180     /* Ball position */
181     int i_ball_x;
182     int i_ball_y;
183
184     int i_ballSpeed;
185
186     int i_ballSize;
187
188     bool b_edgeVisible;
189
190     /* Offsets for YUV packed chroma */
191     int i_y_offset;
192     int i_u_offset;
193     int i_v_offset;
194
195     /* Gradient values */
196     uint32_t *p_smooth;
197     int32_t *p_grad_x;
198     int32_t *p_grad_y;
199
200     /* Gradient threshold */
201     int i_gradThresh;
202
203     /* Motion vectors */
204     float f_lastVect_x;
205     float f_lastVect_y;
206
207     float f_newVect_x;
208     float f_newVect_y;
209
210     float f_contVect_x;
211     float f_contVect_y;
212
213     /* Pointer on drawing function */
214     void ( *drawingPixelFunction )( filter_sys_t *, picture_t *,
215                                     uint8_t, uint8_t, uint8_t,
216                                     int, int, bool );
217 };
218
219
220 /*****************************************************************************
221 * Create: allocates Distort video thread output method
222 *****************************************************************************
223 * This function allocates and initializes a Distort vout method.
224 *****************************************************************************/
225 static int Create( vlc_object_t *p_this )
226 {
227     filter_t *p_filter = (filter_t *)p_this;
228     char *psz_method;
229
230     /* Allocate structure */
231     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
232     if( p_filter->p_sys == NULL )
233         return VLC_ENOMEM;
234
235     switch( p_filter->fmt_in.video.i_chroma )
236     {
237         case VLC_CODEC_I420:
238         case VLC_CODEC_J420:
239             p_filter->p_sys->drawingPixelFunction = drawPixelI420;
240             COLORS_YUV
241             break;
242         CASE_PACKED_YUV_422
243             p_filter->p_sys->drawingPixelFunction = drawPixelPacked;
244             COLORS_YUV
245             GetPackedYuvOffsets( p_filter->fmt_in.video.i_chroma,
246                                  &p_filter->p_sys->i_y_offset,
247                                  &p_filter->p_sys->i_u_offset,
248                                  &p_filter->p_sys->i_v_offset );
249             break;
250         case VLC_CODEC_RGB24:
251             p_filter->p_sys->drawingPixelFunction = drawPixelRGB24;
252             COLORS_RGB
253             break;
254         default:
255             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
256                      (char*)&(p_filter->fmt_in.video.i_chroma) );
257             return VLC_EGENERIC;
258     }
259
260     p_filter->p_sys->p_image = image_HandlerCreate( p_filter );
261     if( p_filter->p_sys->p_image == NULL )
262         return VLC_EGENERIC;
263
264     p_filter->pf_video_filter = Filter;
265
266     config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
267                        p_filter->p_cfg );
268
269     if( !(psz_method =
270         var_CreateGetNonEmptyStringCommand( p_filter,
271                                             FILTER_PREFIX "color" ) ) )
272     {
273         msg_Err( p_filter, "configuration variable "
274                  FILTER_PREFIX "color empty" );
275         p_filter->p_sys->ballColor = RED;
276     }
277     else
278         p_filter->p_sys->ballColor = getBallColor( p_this, psz_method );
279
280     free( psz_method );
281
282     p_filter->p_sys->i_ballSize =
283             var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX "size" );
284     p_filter->p_sys->i_ballSpeed =
285             var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX "speed" );
286     p_filter->p_sys->b_edgeVisible =
287             var_CreateGetBoolCommand( p_filter, FILTER_PREFIX "edge-visible" );
288     p_filter->p_sys->i_gradThresh =
289             var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX "gradient-threshold" );
290
291     vlc_mutex_init( &p_filter->p_sys->lock );
292
293     var_AddCallback( p_filter, FILTER_PREFIX "color",
294                      ballCallback, p_filter->p_sys );
295     var_AddCallback( p_filter, FILTER_PREFIX "size",
296                      ballCallback, p_filter->p_sys );
297     var_AddCallback( p_filter, FILTER_PREFIX "speed",
298                      ballCallback, p_filter->p_sys );
299     var_AddCallback( p_filter, FILTER_PREFIX "edge-visible",
300                      ballCallback, p_filter->p_sys );
301
302     p_filter->p_sys->p_smooth = NULL;
303     p_filter->p_sys->p_grad_x = NULL;
304     p_filter->p_sys->p_grad_y = NULL;
305
306     p_filter->p_sys->i_ball_x = 100;
307     p_filter->p_sys->i_ball_y = 100;
308
309     p_filter->p_sys->f_lastVect_x = 0;
310     p_filter->p_sys->f_lastVect_y = -1;
311
312     return VLC_SUCCESS;
313 }
314
315
316 /*****************************************************************************
317 * Destroy: destroy Distort video thread output method
318 *****************************************************************************
319 * Terminate an output method created by DistortCreateOutputMethod
320  *****************************************************************************/
321 static void Destroy( vlc_object_t *p_this )
322 {
323     filter_t *p_filter = (filter_t *)p_this;
324     filter_sys_t *p_sys = p_filter->p_sys;
325
326     var_DelCallback( p_filter, FILTER_PREFIX "color",
327                      ballCallback, p_sys );
328     var_DelCallback( p_filter, FILTER_PREFIX "size",
329                      ballCallback, p_sys );
330     var_DelCallback( p_filter, FILTER_PREFIX "speed",
331                      ballCallback, p_sys );
332     var_DelCallback( p_filter, FILTER_PREFIX "edge-visible",
333                      ballCallback, p_sys );
334
335     vlc_mutex_destroy( &p_sys->lock );
336
337     image_HandlerDelete( p_sys->p_image );
338
339     free( p_sys->p_smooth );
340     free( p_sys->p_grad_x );
341     free( p_sys->p_grad_y );
342
343     free( p_sys );
344 }
345
346
347 /*****************************************************************************
348 * Render: displays previously rendered output
349 *****************************************************************************
350 * This function send the currently rendered image to Distort image, waits
351 * until it is displayed and switch the two rendering buffers, preparing next
352 * frame.
353  *****************************************************************************/
354 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
355 {
356     picture_t *p_outpic;
357
358     if( !p_pic ) return NULL;
359
360     p_outpic = filter_NewPicture( p_filter );
361     if( !p_outpic )
362     {
363         picture_Release( p_pic );
364         return NULL;
365     }
366
367     vlc_mutex_lock( &p_filter->p_sys->lock );
368     FilterBall( p_filter, p_pic, p_outpic );
369     vlc_mutex_unlock( &p_filter->p_sys->lock );
370
371     return CopyInfoAndRelease( p_outpic, p_pic );
372 }
373
374
375 /*****************************************************************************
376 * Drawing functions
377 *****************************************************************************/
378
379 static void drawBall( filter_sys_t *p_sys, picture_t *p_outpic )
380 {
381     int x = p_sys->i_ball_x;
382     int y = p_sys->i_ball_y;
383     int size = p_sys->i_ballSize;
384
385     const int i_width = p_outpic->p[0].i_visible_pitch;
386     const int i_height = p_outpic->p[0].i_visible_lines;
387
388     for( int j = y - size; j <= y + size; j++ )
389     {
390         bool b_skip = ( x - size ) % 2;
391         for( int i = x - size; i <= x + size; i++ )
392         {
393             /* Draw the pixel if it is inside the disk
394                and check we don't write out the frame. */
395             if( ( i - x ) * ( i - x ) + ( j - y ) * ( j - y ) <= size * size
396                 && i >= 0 && i < i_width
397                 && j >= 0 && j < i_height )
398             {
399                 ( *p_sys->drawingPixelFunction )( p_sys, p_outpic,
400                                     colorList[ p_sys->ballColor ].comp1,
401                                     colorList[ p_sys->ballColor ].comp2,
402                                     colorList[ p_sys->ballColor ].comp3,
403                                     i, j, b_skip );
404             }
405             b_skip = !b_skip;
406         }
407     }
408 }
409
410
411 static void drawPixelRGB24( filter_sys_t *p_sys, picture_t *p_outpic,
412                             uint8_t R, uint8_t G, uint8_t B,
413                             int x, int y, bool b_skip )
414 {
415     VLC_UNUSED( p_sys );
416     VLC_UNUSED( b_skip );
417     uint8_t *p_pixel = p_outpic->p[0].p_pixels
418                        + p_outpic->p[0].i_pitch
419                        * x + 3 * y;
420     *p_pixel = B;
421     *++p_pixel = G;
422     *++p_pixel = R;
423 }
424
425
426 static void drawPixelI420( filter_sys_t *p_sys, picture_t *p_outpic,
427                            uint8_t Y, uint8_t U, uint8_t V,
428                            int x, int y, bool b_skip )
429 {
430     VLC_UNUSED( p_sys );
431     *( p_outpic->p[0].p_pixels + p_outpic->p[0].i_pitch * y + x ) = Y;
432     if( !b_skip )
433     {
434         *( p_outpic->p[2].p_pixels + p_outpic->p[2].i_pitch
435                                      * ( y / 2 ) + x / 2 ) = U;
436         *( p_outpic->p[1].p_pixels + p_outpic->p[1].i_pitch
437                                      * ( y / 2 ) + x / 2 ) = V;
438     }
439 }
440
441
442 static void drawPixelPacked( filter_sys_t *p_sys, picture_t *p_outpic,
443                            uint8_t Y, uint8_t U, uint8_t V,
444                            int x, int y, bool b_skip )
445 {
446     uint8_t *p_pixel = p_outpic->p[0].p_pixels
447                        + p_outpic->p[0].i_pitch * y + x * 2;
448     *( p_pixel + p_sys->i_y_offset ) = Y;
449     if( !b_skip )
450     {
451         *( p_pixel + p_sys->i_u_offset ) = U;
452         *( p_pixel + p_sys->i_v_offset ) = V;
453     }
454 }
455
456
457 /*****************************************************************************
458 * Nomalize vector
459 *****************************************************************************
460 * Modify its value to set its norm to 1 and keep its direction.
461  *****************************************************************************/
462 static void NormalizeVector( float *vect_x, float *vect_y )
463 {
464     float norm = sqrt( *vect_x * *vect_x + *vect_y * *vect_y );
465     if( *vect_x != 0 || *vect_y != 0 )
466     {
467         *vect_x /= norm;
468         *vect_y /= norm;
469     }
470 }
471
472
473 /*****************************************************************************
474 * Gaussian Convolution
475 *****************************************************************************
476 *    Gaussian convolution ( sigma == 1.4 )
477 *
478 *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
479 *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
480 *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
481 *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
482 *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
483  *****************************************************************************/
484 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
485 {
486     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
487     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
488     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
489     const int i_numLines = p_inpic->p[Y_PLANE].i_visible_lines;
490
491     int x,y;
492     for( y = 2; y < i_numLines - 2; y++ )
493     {
494         for( x = 2; x < i_src_visible - 2; x++ )
495         {
496             p_smooth[y*i_src_visible+x] = (uint32_t)(
497                     /* 2 rows up */
498                     ( p_inpix[(y-2)*i_src_pitch+x-2] )
499                     + ((p_inpix[(y-2)*i_src_pitch+x-1]
500                     +   p_inpix[(y-2)*i_src_pitch+x]
501                     +   p_inpix[(y-2)*i_src_pitch+x+1])<<1 )
502                     + ( p_inpix[(y-2)*i_src_pitch+x+2] )
503                     /* 1 row up */
504                     + ((p_inpix[(y-1)*i_src_pitch+x-2]
505                     + ( p_inpix[(y-1)*i_src_pitch+x-1]<<1 )
506                     + ( p_inpix[(y-1)*i_src_pitch+x]*3 )
507                     + ( p_inpix[(y-1)*i_src_pitch+x+1]<<1 )
508                     +   p_inpix[(y-1)*i_src_pitch+x+2]
509                     /* */
510                     +   p_inpix[y*i_src_pitch+x-2]
511                     + ( p_inpix[y*i_src_pitch+x-1]*3 )
512                     + ( p_inpix[y*i_src_pitch+x]<<2 )
513                     + ( p_inpix[y*i_src_pitch+x+1]*3 )
514                     +   p_inpix[y*i_src_pitch+x+2]
515                     /* 1 row down */
516                     +   p_inpix[(y+1)*i_src_pitch+x-2]
517                     + ( p_inpix[(y+1)*i_src_pitch+x-1]<<1 )
518                     + ( p_inpix[(y+1)*i_src_pitch+x]*3 )
519                     + ( p_inpix[(y+1)*i_src_pitch+x+1]<<1 )
520                     +   p_inpix[(y+1)*i_src_pitch+x+2] )<<1 )
521                     /* 2 rows down */
522                     + ( p_inpix[(y+2)*i_src_pitch+x-2] )
523                     + ((p_inpix[(y+2)*i_src_pitch+x-1]
524                     +   p_inpix[(y+2)*i_src_pitch+x]
525                     +   p_inpix[(y+2)*i_src_pitch+x+1])<<1 )
526                     + ( p_inpix[(y+2)*i_src_pitch+x+2] )
527                                                     ) >> 6 /* 115 */;
528         }
529     }
530 }
531
532
533 /*****************************************************************************
534  * FilterBall: Augmented reality ball video filter
535  *****************************************************************************
536  * The edge detection part comes from gradient.c video filter module.
537  * The Canny edge detection algorithm is used :
538  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
539  * (well ... the implementation isn't really the canny algorithm ... but some
540  * ideas are the same)
541  *****************************************************************************/
542 static void FilterBall( filter_t *p_filter, picture_t *p_inpic,
543                            picture_t *p_outpic )
544 {
545     int x, y;
546     filter_sys_t *p_sys = p_filter->p_sys;
547
548     uint32_t *p_smooth;
549     int32_t *p_grad_x;
550     int32_t *p_grad_y;
551
552     picture_t *p_converted;
553     video_format_t fmt_comp;
554
555     switch( p_filter->fmt_in.video.i_chroma )
556     {
557         case VLC_CODEC_RGB24:
558         CASE_PACKED_YUV_422
559             fmt_comp.i_width = p_filter->fmt_in.video.i_width;
560             fmt_comp.i_height = p_filter->fmt_in.video.i_height;
561             fmt_comp.i_chroma = VLC_FOURCC('G','R','E','Y');
562             fmt_comp.i_visible_width = fmt_comp.i_width;
563             fmt_comp.i_visible_height = fmt_comp.i_height;
564
565             p_converted = image_Convert( p_filter->p_sys->p_image, p_inpic,
566                                          &(p_filter->fmt_in.video),
567                                          &fmt_comp );
568             if( !p_converted )
569                 return;
570
571             break;
572
573         default:
574             p_converted = p_inpic;
575             break;
576     }
577
578     const int i_numCols = p_converted->p[0].i_visible_pitch;
579     const int i_numLines = p_converted->p[0].i_visible_lines;
580
581     if( !p_filter->p_sys->p_smooth )
582         p_filter->p_sys->p_smooth =
583                 (uint32_t *)malloc( i_numLines * i_numCols
584                                     * sizeof(uint32_t));
585     p_smooth = p_filter->p_sys->p_smooth;
586
587     if( !p_filter->p_sys->p_grad_x )
588         p_filter->p_sys->p_grad_x =
589                 (int32_t *)malloc( i_numLines * i_numCols
590                                    * sizeof(int32_t));
591     p_grad_x = p_filter->p_sys->p_grad_x;
592
593     if( !p_filter->p_sys->p_grad_y )
594         p_filter->p_sys->p_grad_y =
595                 (int32_t *)malloc( i_numLines * i_numCols
596                                    * sizeof(int32_t));
597     p_grad_y = p_filter->p_sys->p_grad_y;
598
599     if( !p_smooth || !p_grad_x || !p_grad_y ) return;
600
601     vlc_memcpy( p_outpic->p[0].p_pixels, p_inpic->p[0].p_pixels,
602                 p_outpic->p[0].i_lines * p_outpic->p[0].i_pitch );
603     vlc_memcpy( p_outpic->p[1].p_pixels, p_inpic->p[1].p_pixels,
604                 p_outpic->p[1].i_lines * p_outpic->p[1].i_pitch );
605     vlc_memcpy( p_outpic->p[2].p_pixels, p_inpic->p[2].p_pixels,
606                 p_outpic->p[2].i_lines * p_outpic->p[2].i_pitch );
607
608     GaussianConvolution( p_converted, p_smooth );
609
610     /* Compute the picture Sobel gradient
611        | -1 0 1 |     |  1  2  1 |
612        | -2 0 2 | and |  0  0  0 |
613        | -1 0 1 |     | -1 -2 -1 | */
614
615     for( y = 1; y < i_numLines - 1; y++ )
616     {
617         for( x = 1; x < i_numCols - 1; x++ )
618         {
619
620             p_grad_x[ y * i_numCols + x ] =
621                     ( p_smooth[(y-1)*i_numCols+x-1]
622                     - p_smooth[(y+1)*i_numCols+x-1] )
623                     + ( ( p_smooth[(y-1)*i_numCols+x]
624                     - p_smooth[(y+1)*i_numCols+x] ) <<1 )
625                     + ( p_smooth[(y-1)*i_numCols+x+1]
626                     - p_smooth[(y+1)*i_numCols+x+1] );
627             p_grad_y[ y * i_numCols + x ] =
628                     ( p_smooth[(y-1)*i_numCols+x-1]
629                     - p_smooth[(y-1)*i_numCols+x+1] )
630                     + ( ( p_smooth[y*i_numCols+x-1]
631                     - p_smooth[y*i_numCols+x+1] ) <<1 )
632                     + ( p_smooth[(y+1)*i_numCols+x-1]
633                     - p_smooth[(y+1)*i_numCols+x+1] );
634         }
635     }
636
637     if( p_sys->b_edgeVisible )
638     {
639         /* Display the edges. */
640         for( y = 1; y < i_numLines - 1; y++ )
641         {
642             for( x = 1; x < i_numCols - 1; x++ )
643             {
644                 if( abs( p_grad_x[ y * i_numCols + x ] )
645                     + abs( p_grad_y[ y * i_numCols + x ] )
646                     > p_sys->i_gradThresh )
647                 {
648                     ( *p_sys->drawingPixelFunction )( p_sys, p_outpic,
649                                                       colorList[ WHITE ].comp1,
650                                                       colorList[ WHITE ].comp2,
651                                                       colorList[ WHITE ].comp3,
652                                                       x, y, 0 );
653                 }
654             }
655         }
656     }
657
658     int i_motion;
659
660     float *pf_lastVect_x = &p_sys->f_lastVect_x;
661     float *pf_lastVect_y = &p_sys->f_lastVect_y;
662
663     float f_newVect_x = 0;
664     float f_newVect_y = 0;
665     float f_contVect_x = 0;
666     float f_contVect_y = 0;
667
668     int nb_collisions = 0;
669
670     bool bounce = false;
671
672     /* Test collisions for each pixel the ball will cover in its
673        motion. */
674     for ( i_motion = 0; i_motion <= p_sys->i_ballSpeed && !bounce; i_motion++ )
675     {
676         /* Compute next ball position */
677         x = roundf( (float)p_sys->i_ball_x
678                     + *pf_lastVect_x * (float)i_motion );
679         y = roundf( (float)p_sys->i_ball_y
680                     + *pf_lastVect_y * (float)i_motion );
681
682         for( int i = x - p_sys->i_ballSize; i <= x + p_sys->i_ballSize; i++ )
683         {
684             for( int j = y - p_sys->i_ballSize;
685                  j <= y + p_sys->i_ballSize; j++ )
686             {
687                 /* Test the pixel if it is inside the disk and check we don't
688                 write out the frame. */
689                 if( ( i - x ) * ( i - x ) + ( j - y ) * ( j - y )
690                     == p_sys->i_ballSize * p_sys->i_ballSize
691                     && j <= i_numLines - 1 && x <= i_numCols - 1
692                     && j >= 0 && i >= 0 )
693                 {
694                     /* Test firstly the picture limit collisions. */
695                     if( i <= 2 )
696                     {
697                         f_contVect_x = x - i;
698                         f_contVect_y = 0;
699                         x++;
700                         bounce = true;
701                         nb_collisions = 1;
702                         goto endLoop;
703                     }
704                     if( j <= 2 )
705                     {
706                         f_contVect_x = 0;
707                         f_contVect_y = y - j;
708                         y++;
709                         bounce = true;
710                         nb_collisions = 1;
711                         goto endLoop;
712                     }
713                     if( j >= i_numLines - 3 )
714                     {
715                         f_contVect_x = 0;
716                         f_contVect_y = y - j;
717                         y--;
718                         bounce = true;
719                         nb_collisions = 1;
720                         goto endLoop;
721                     }
722                     if( i >= i_numCols - 3 )
723                     {
724                         f_contVect_x = x - i;
725                         f_contVect_y = 0;
726                         x--;
727                         bounce = true;
728                         nb_collisions = 1;
729                         goto endLoop;
730                     }
731                     /* Test the collisions with edges. */
732                     if( abs( p_grad_x[ j * i_numCols + i ] )
733                         + abs( p_grad_y[ j * i_numCols + i ] )
734                         > p_sys->i_gradThresh )
735                     {
736                         f_contVect_x += x - i;
737                         f_contVect_y += y - j;
738                         nb_collisions++;
739                         bounce = true;
740                     }
741                 }
742             }
743         }
744     }
745
746     endLoop:
747
748     if( bounce )
749     {
750         /* Compute normal vector. */
751         f_contVect_x /= nb_collisions;
752         f_contVect_y /= nb_collisions;
753         NormalizeVector( &f_contVect_x, &f_contVect_y );
754
755         /* Compute the new vector after the bounce. */
756         float cosinus = *pf_lastVect_x * f_contVect_x
757                         + *pf_lastVect_y * f_contVect_y;
758         f_newVect_x = *pf_lastVect_x - 2 * cosinus * f_contVect_x;
759         f_newVect_y = *pf_lastVect_y - 2 * cosinus * f_contVect_y;
760         NormalizeVector( &f_newVect_x, &f_newVect_y );
761
762         *pf_lastVect_x = f_newVect_x;
763         *pf_lastVect_y = f_newVect_y;
764
765         p_sys->i_ball_x = x;
766         p_sys->i_ball_y = y;
767
768         /* Test if next pixel is outside the frame limits.
769            If it is the case, then the ball is blocked until it can move. */
770         x = roundf( (float)x + *pf_lastVect_x );
771         y = roundf( (float)y + *pf_lastVect_y );
772         if( x - p_sys->i_ballSize < 2
773             || x + p_sys->i_ballSize > i_numCols - 3
774             || y - p_sys->i_ballSize < 2
775             || y + p_sys->i_ballSize > i_numLines - 3 )
776         {
777             *pf_lastVect_x = 0;
778             *pf_lastVect_y = 0;
779         }
780         else
781             /* After a bouce, the first ball motion is always one pixel. */
782             i_motion = p_sys->i_ballSpeed - 1;
783     }
784     else
785         i_motion = 0;
786
787     /* Compute next ball position. */
788     p_sys->i_ball_x = roundf( (float)p_sys->i_ball_x + *pf_lastVect_x
789                               * (float)( p_sys->i_ballSpeed - i_motion ) );
790     p_sys->i_ball_y = roundf( p_sys->i_ball_y + *pf_lastVect_y
791                               * (float)( p_sys->i_ballSpeed - i_motion ) );
792
793     /* Draw the ball */
794     drawBall( p_sys, p_outpic );
795
796     switch( p_filter->fmt_in.video.i_chroma )
797     {
798         case VLC_CODEC_RGB24:
799         CASE_PACKED_YUV_422
800             picture_Release( p_converted );
801         default:
802             break;
803     }
804 }
805
806
807 /*****************************************************************************
808  * ballCallback
809  *****************************************************************************
810  * filter parameter modification callback
811  *****************************************************************************/
812 static int ballCallback( vlc_object_t *p_this, char const *psz_var,
813                              vlc_value_t oldval, vlc_value_t newval,
814                              void *p_data )
815 {
816     VLC_UNUSED(oldval);
817     filter_sys_t *p_sys = (filter_sys_t *)p_data;
818     msg_Err( p_this, "Test" );
819
820     vlc_mutex_lock( &p_sys->lock );
821     if( !strcmp( psz_var, FILTER_PREFIX "color" ) )
822     {
823         p_sys->ballColor = getBallColor( p_this, newval.psz_string );
824     }
825     else if( !strcmp( psz_var, FILTER_PREFIX "size" ) )
826     {
827         p_sys->i_ballSize = newval.i_int;
828     }
829     else if( !strcmp( psz_var, FILTER_PREFIX "speed" ) )
830     {
831         p_sys->i_ballSpeed = newval.i_int;
832     }
833     else if( !strcmp( psz_var, FILTER_PREFIX "edge-visible" ) )
834     {
835         p_sys->b_edgeVisible = newval.b_bool;
836     }
837     else if( !strcmp( psz_var, FILTER_PREFIX "gradient-threshold" ) )
838     {
839         p_sys->i_gradThresh = newval.i_int;
840     }
841     vlc_mutex_unlock( &p_sys->lock );
842
843     return VLC_SUCCESS;
844 }
845
846
847 /*****************************************************************************
848  * getBallColor
849  *****************************************************************************
850  * Get and assign the ball color value
851  *****************************************************************************/
852 static int getBallColor( vlc_object_t *p_this, char const *psz_newval )
853 {
854     int ret;
855     if( !strcmp( psz_newval, "red" ) )
856         ret = RED;
857     else if( !strcmp( psz_newval, "blue" ) )
858         ret = BLUE;
859     else if( !strcmp( psz_newval, "green" ) )
860         ret = GREEN;
861     else if( !strcmp( psz_newval, "white" ) )
862         ret = WHITE;
863     else
864     {
865         msg_Err( p_this, "no valid ball color provided (%s)", psz_newval );
866         ret = RED;
867     }
868     return ret;
869 }