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