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