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