1 /*****************************************************************************
2 * ball.c : Augmented reality ball video filter module
3 *****************************************************************************
4 * Copyright (C) 2000-2009 the VideoLAN team
6 * Author: Adrien Maglo <magsoft@videolan.org>
8 * The Canny edge detection algorithm comes from gradient.c which was
10 * Samuel Hocevar <sam@zoy.org>
11 * Antoine Cellerier <dionoea -at- videolan -dot- org>
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.
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.
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 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
37 #include <math.h> /* sin(), cos(), asin() */
39 #include <vlc_common.h>
40 #include <vlc_plugin.h>
42 #include "vlc_filter.h"
43 #include "filter_picture.h"
44 #include "vlc_image.h"
46 enum { RED, GREEN, BLUE, WHITE };
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;
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;
69 /*****************************************************************************
71 *****************************************************************************/
72 static int Create ( vlc_object_t * );
73 static void Destroy ( vlc_object_t * );
75 static picture_t *Filter( filter_t *, picture_t * );
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 );
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,
92 static int getBallColor( vlc_object_t *p_this, char const *psz_newval );
95 /*****************************************************************************
97 *****************************************************************************/
98 #define BALL_COLOR_TEXT N_("Ball color")
99 #define BALL_COLOR_LONGTEXT N_("Ball color, one of \"red\", \"blue\" and \"green\".")
101 #define EDGE_VISIBLE_TEXT N_("Edge visible")
102 #define EDGE_VISIBLE_LONGTEXT N_("Set edge visibility.")
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.")
108 #define BALL_SIZE_TEXT N_("Ball size")
109 #define BALL_SIZE_LONGTEXT N_("Set ball size giving its radius in number \
112 #define GRAD_THRESH_TEXT N_("Gradient threshold")
113 #define GRAD_THRESH_LONGTEXT N_("Set gradient threshold for edge computation.")
115 #define BALL_HELP N_("Augmented reality ball game")
117 #define FILTER_PREFIX "ball-"
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") };
124 set_description( N_("Ball video filter") )
125 set_shortname( N_( "Ball" ))
127 set_capability( "video filter2", 0 )
128 set_category( CAT_VIDEO )
129 set_subcategory( SUBCAT_VIDEO_VFILTER )
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 )
135 add_integer_with_range( FILTER_PREFIX "speed", 4, 1, 15,
136 BALL_SPEED_TEXT, BALL_SPEED_LONGTEXT, false )
138 add_integer_with_range( FILTER_PREFIX "size", 10, 5, 30,
139 BALL_SIZE_TEXT, BALL_SIZE_LONGTEXT, false )
141 add_integer_with_range( FILTER_PREFIX "gradient-threshold", 40, 1, 200,
142 GRAD_THRESH_TEXT, GRAD_THRESH_LONGTEXT, false )
144 add_bool( FILTER_PREFIX "edge-visible", true,
145 EDGE_VISIBLE_TEXT, EDGE_VISIBLE_LONGTEXT, true )
147 add_shortcut( "ball" )
148 set_callbacks( Create, Destroy )
151 static const char *const ppsz_filter_options[] = {
152 "ball-color", "ball-speed", "ball-size",
153 "gradient-threshold", "edge-visible", NULL
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 *****************************************************************************/
169 image_handler_t *p_image;
181 /* Offsets for YUV packed chroma */
186 /* Gradient values */
191 /* Gradient threshold */
204 /* Pointer on drawing function */
205 void ( *drawingPixelFunction )( filter_sys_t *, picture_t *,
206 uint8_t, uint8_t, uint8_t,
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 )
224 filter_t *p_filter = (filter_t *)p_this;
227 /* Allocate structure */
228 p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
229 if( p_filter->p_sys == NULL )
232 switch( p_filter->fmt_in.video.i_chroma )
236 p_filter->p_sys->drawingPixelFunction = drawPixelI420;
240 p_filter->p_sys->drawingPixelFunction = drawPixelPacked;
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 );
247 case VLC_CODEC_RGB24:
248 p_filter->p_sys->drawingPixelFunction = drawPixelRGB24;
252 msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
253 (char*)&(p_filter->fmt_in.video.i_chroma) );
257 p_filter->p_sys->p_image = image_HandlerCreate( p_filter );
258 if( p_filter->p_sys->p_image == NULL )
261 p_filter->pf_video_filter = Filter;
263 config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
267 var_CreateGetNonEmptyStringCommand( p_filter,
268 FILTER_PREFIX "color" ) ) )
270 msg_Err( p_filter, "configuration variable "
271 FILTER_PREFIX "color empty" );
272 p_filter->p_sys->ballColor = RED;
275 p_filter->p_sys->ballColor = getBallColor( p_this, psz_method );
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" );
288 vlc_mutex_init( &p_filter->p_sys->lock );
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 );
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;
303 p_filter->p_sys->i_ball_x = 100;
304 p_filter->p_sys->i_ball_y = 100;
306 p_filter->p_sys->f_lastVect_x = 0;
307 p_filter->p_sys->f_lastVect_y = -1;
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 )
320 filter_t *p_filter = (filter_t *)p_this;
321 filter_sys_t *p_sys = p_filter->p_sys;
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 );
332 vlc_mutex_destroy( &p_sys->lock );
334 image_HandlerDelete( p_sys->p_image );
336 free( p_sys->p_smooth );
337 free( p_sys->p_grad_x );
338 free( p_sys->p_grad_y );
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
350 *****************************************************************************/
351 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
355 if( !p_pic ) return NULL;
357 p_outpic = filter_NewPicture( p_filter );
360 picture_Release( p_pic );
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 );
368 return CopyInfoAndRelease( p_outpic, p_pic );
372 /*****************************************************************************
374 *****************************************************************************/
376 static void drawBall( filter_sys_t *p_sys, picture_t *p_outpic )
378 int x = p_sys->i_ball_x;
379 int y = p_sys->i_ball_y;
380 int size = p_sys->i_ballSize;
382 const int i_width = p_outpic->p[0].i_visible_pitch;
383 const int i_height = p_outpic->p[0].i_visible_lines;
385 for( int j = y - size; j <= y + size; j++ )
387 bool b_skip = ( x - size ) % 2;
388 for( int i = x - size; i <= x + size; i++ )
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 )
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,
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 )
413 VLC_UNUSED( b_skip );
414 uint8_t *p_pixel = p_outpic->p[0].p_pixels
415 + p_outpic->p[0].i_pitch
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 )
428 *( p_outpic->p[0].p_pixels + p_outpic->p[0].i_pitch * y + x ) = Y;
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;
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 )
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;
448 *( p_pixel + p_sys->i_u_offset ) = U;
449 *( p_pixel + p_sys->i_v_offset ) = V;
454 /*****************************************************************************
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 )
461 float norm = sqrt( *vect_x * *vect_x + *vect_y * *vect_y );
462 if( *vect_x != 0 || *vect_y != 0 )
470 /*****************************************************************************
471 * Gaussian Convolution
472 *****************************************************************************
473 * Gaussian convolution ( sigma == 1.4 )
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 )
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;
489 for( y = 2; y < i_numLines - 2; y++ )
491 for( x = 2; x < i_src_visible - 2; x++ )
493 p_smooth[y*i_src_visible+x] = (uint32_t)(
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] )
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]
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]
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 )
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] )
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 )
543 filter_sys_t *p_sys = p_filter->p_sys;
549 picture_t *p_converted;
550 video_format_t fmt_comp;
552 switch( p_filter->fmt_in.video.i_chroma )
554 case VLC_CODEC_RGB24:
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;
562 p_converted = image_Convert( p_filter->p_sys->p_image, p_inpic,
563 &(p_filter->fmt_in.video),
571 p_converted = p_inpic;
575 const int i_numCols = p_converted->p[0].i_visible_pitch;
576 const int i_numLines = p_converted->p[0].i_visible_lines;
578 if( !p_filter->p_sys->p_smooth )
579 p_filter->p_sys->p_smooth =
580 (uint32_t *)malloc( i_numLines * i_numCols
582 p_smooth = p_filter->p_sys->p_smooth;
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
588 p_grad_x = p_filter->p_sys->p_grad_x;
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
594 p_grad_y = p_filter->p_sys->p_grad_y;
596 if( !p_smooth || !p_grad_x || !p_grad_y ) return;
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 );
605 GaussianConvolution( p_converted, p_smooth );
607 /* Compute the picture Sobel gradient
609 | -2 0 2 | and | 0 0 0 |
610 | -1 0 1 | | -1 -2 -1 | */
612 for( y = 1; y < i_numLines - 1; y++ )
614 for( x = 1; x < i_numCols - 1; x++ )
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] );
634 if( p_sys->b_edgeVisible )
636 /* Display the edges. */
637 for( y = 1; y < i_numLines - 1; y++ )
639 for( x = 1; x < i_numCols - 1; x++ )
641 if( abs( p_grad_x[ y * i_numCols + x ] )
642 + abs( p_grad_y[ y * i_numCols + x ] )
643 > p_sys->i_gradThresh )
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,
657 float *pf_lastVect_x = &p_sys->f_lastVect_x;
658 float *pf_lastVect_y = &p_sys->f_lastVect_y;
660 float f_newVect_x = 0;
661 float f_newVect_y = 0;
662 float f_contVect_x = 0;
663 float f_contVect_y = 0;
665 int nb_collisions = 0;
669 /* Test collisions for each pixel the ball will cover in its
671 for ( i_motion = 0; i_motion <= p_sys->i_ballSpeed && !bounce; i_motion++ )
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 );
679 for( int i = x - p_sys->i_ballSize; i <= x + p_sys->i_ballSize; i++ )
681 for( int j = y - p_sys->i_ballSize;
682 j <= y + p_sys->i_ballSize; j++ )
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 )
691 /* Test firstly the picture limit collisions. */
694 f_contVect_x = x - i;
704 f_contVect_y = y - j;
710 if( j >= i_numLines - 3 )
713 f_contVect_y = y - j;
719 if( i >= i_numCols - 3 )
721 f_contVect_x = x - i;
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 )
733 f_contVect_x += x - i;
734 f_contVect_y += y - j;
747 /* Compute normal vector. */
748 f_contVect_x /= nb_collisions;
749 f_contVect_y /= nb_collisions;
750 NormalizeVector( &f_contVect_x, &f_contVect_y );
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 );
759 *pf_lastVect_x = f_newVect_x;
760 *pf_lastVect_y = f_newVect_y;
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 )
778 /* After a bouce, the first ball motion is always one pixel. */
779 i_motion = p_sys->i_ballSpeed - 1;
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 ) );
791 drawBall( p_sys, p_outpic );
793 switch( p_filter->fmt_in.video.i_chroma )
795 case VLC_CODEC_RGB24:
797 picture_Release( p_converted );
804 /*****************************************************************************
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,
814 filter_sys_t *p_sys = (filter_sys_t *)p_data;
815 msg_Err( p_this, "Test" );
817 vlc_mutex_lock( &p_sys->lock );
818 if( !strcmp( psz_var, FILTER_PREFIX "color" ) )
820 p_sys->ballColor = getBallColor( p_this, newval.psz_string );
822 else if( !strcmp( psz_var, FILTER_PREFIX "size" ) )
824 p_sys->i_ballSize = newval.i_int;
826 else if( !strcmp( psz_var, FILTER_PREFIX "speed" ) )
828 p_sys->i_ballSpeed = newval.i_int;
830 else if( !strcmp( psz_var, FILTER_PREFIX "edge-visible" ) )
832 p_sys->b_edgeVisible = newval.b_bool;
834 else if( !strcmp( psz_var, FILTER_PREFIX "gradient-threshold" ) )
836 p_sys->i_gradThresh = newval.i_int;
838 vlc_mutex_unlock( &p_sys->lock );
844 /*****************************************************************************
846 *****************************************************************************
847 * Get and assign the ball color value
848 *****************************************************************************/
849 static int getBallColor( vlc_object_t *p_this, char const *psz_newval )
852 if( !strcmp( psz_newval, "red" ) )
854 else if( !strcmp( psz_newval, "blue" ) )
856 else if( !strcmp( psz_newval, "green" ) )
858 else if( !strcmp( psz_newval, "white" ) )
862 msg_Err( p_this, "no valid ball color provided (%s)", psz_newval );