1 /*****************************************************************************
2 * ball.c : Augmented reality ball video filter module
3 *****************************************************************************
4 * Copyright (C) 2000-2009 VLC authors and VideoLAN
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 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.
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.
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 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
37 #include <math.h> /* sin(), cos(), asin() */
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>
46 enum { RED
, GREEN
, BLUE
, WHITE
};
49 p_sys->colorList[RED].comp1 = 255; p_sys->colorList[RED].comp2 = 0; \
50 p_sys->colorList[RED].comp3 = 0; \
51 p_sys->colorList[GREEN].comp1 = 0; p_sys->colorList[GREEN].comp2 = 255; \
52 p_sys->colorList[GREEN].comp3 = 0; \
53 p_sys->colorList[BLUE].comp1 = 0; p_sys->colorList[BLUE].comp2 = 0; \
54 p_sys->colorList[BLUE].comp3 = 255; \
55 p_sys->colorList[WHITE].comp1 = 255; p_sys->colorList[WHITE].comp2 = 255; \
56 p_sys->colorList[WHITE].comp3 = 255;
59 p_sys->colorList[RED].comp1 = 82; p_sys->colorList[RED].comp2 = 240; \
60 p_sys->colorList[RED].comp3 = 90; \
61 p_sys->colorList[GREEN].comp1 = 145; p_sys->colorList[GREEN].comp2 = 34; \
62 p_sys->colorList[GREEN].comp3 = 54 ; \
63 p_sys->colorList[BLUE].comp1 = 41; p_sys->colorList[BLUE].comp2 = 146; \
64 p_sys->colorList[BLUE].comp3 = 240; \
65 p_sys->colorList[WHITE].comp1 = 255; p_sys->colorList[WHITE].comp2 = 128; \
66 p_sys->colorList[WHITE].comp3 = 128;
69 /*****************************************************************************
71 *****************************************************************************/
73 typedef struct filter_sys_t filter_sys_t
;
75 static int Create ( vlc_object_t
* );
76 static void Destroy ( vlc_object_t
* );
78 static picture_t
*Filter( filter_t
*, picture_t
* );
80 static void drawBall( filter_sys_t
*p_sys
, picture_t
*p_outpic
);
81 static void drawPixelRGB24( filter_sys_t
*p_sys
, picture_t
*p_outpic
,
82 uint8_t R
, uint8_t G
, uint8_t B
,
83 int x
, int y
, bool b_skip
);
84 static void drawPixelI420( 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 static void drawPixelPacked( filter_sys_t
*p_sys
, picture_t
*p_outpic
,
88 uint8_t Y
, uint8_t U
, uint8_t V
,
89 int x
, int y
, bool b_skip
);
91 static void FilterBall( filter_t
*, picture_t
*, picture_t
* );
92 static int ballCallback( vlc_object_t
*, char const *,
93 vlc_value_t
, vlc_value_t
,
95 static int getBallColor( vlc_object_t
*p_this
, char const *psz_newval
);
98 /*****************************************************************************
100 *****************************************************************************/
101 #define BALL_COLOR_TEXT N_("Ball color")
103 #define EDGE_VISIBLE_TEXT N_("Edge visible")
104 #define EDGE_VISIBLE_LONGTEXT N_("Set edge visibility.")
106 #define BALL_SPEED_TEXT N_("Ball speed")
107 #define BALL_SPEED_LONGTEXT N_("Set ball speed, the displacement value " \
108 "in number of pixels by frame.")
110 #define BALL_SIZE_TEXT N_("Ball size")
111 #define BALL_SIZE_LONGTEXT N_("Set ball size giving its radius in number " \
114 #define GRAD_THRESH_TEXT N_("Gradient threshold")
115 #define GRAD_THRESH_LONGTEXT N_("Set gradient threshold for edge computation.")
117 #define BALL_HELP N_("Augmented reality ball game")
119 #define FILTER_PREFIX "ball-"
121 static const char *const mode_list
[] = { "red", "green", "blue", "white" };
122 static const char *const mode_list_text
[] = { N_("Red"), N_("Green"),
123 N_("Blue"), N_("White") };
126 set_description( N_("Ball video filter") )
127 set_shortname( N_( "Ball" ))
129 set_capability( "video filter", 0 )
130 set_category( CAT_VIDEO
)
131 set_subcategory( SUBCAT_VIDEO_VFILTER
)
133 add_string( FILTER_PREFIX
"color", "red",
134 BALL_COLOR_TEXT
, BALL_COLOR_TEXT
, false )
135 change_string_list( mode_list
, mode_list_text
)
137 add_integer_with_range( FILTER_PREFIX
"speed", 4, 1, 15,
138 BALL_SPEED_TEXT
, BALL_SPEED_LONGTEXT
, false )
140 add_integer_with_range( FILTER_PREFIX
"size", 10, 5, 30,
141 BALL_SIZE_TEXT
, BALL_SIZE_LONGTEXT
, false )
143 add_integer_with_range( FILTER_PREFIX
"gradient-threshold", 40, 1, 200,
144 GRAD_THRESH_TEXT
, GRAD_THRESH_LONGTEXT
, false )
146 add_bool( FILTER_PREFIX
"edge-visible", true,
147 EDGE_VISIBLE_TEXT
, EDGE_VISIBLE_LONGTEXT
, true )
149 add_shortcut( "ball" )
150 set_callbacks( Create
, Destroy
)
153 static const char *const ppsz_filter_options
[] = {
154 "color", "speed", "size",
155 "gradient-threshold", "edge-visible", NULL
159 /*****************************************************************************
160 * filter_sys_t: Distort video output method descriptor
161 *****************************************************************************
162 * This structure is part of the video output thread descriptor.
163 * It describes the Distort specific properties of an output thread.
164 *****************************************************************************/
171 image_handler_t
*p_image
;
183 /* Offsets for YUV packed chroma */
188 /* Gradient values */
193 /* Gradient threshold */
206 /* Pointer on drawing function */
207 void ( *drawingPixelFunction
)( filter_sys_t
*, picture_t
*,
208 uint8_t, uint8_t, uint8_t,
219 /*****************************************************************************
220 * Create: allocates Distort video thread output method
221 *****************************************************************************
222 * This function allocates and initializes a Distort vout method.
223 *****************************************************************************/
224 static int Create( vlc_object_t
*p_this
)
226 filter_t
*p_filter
= (filter_t
*)p_this
;
229 /* Allocate structure */
230 filter_sys_t
*p_sys
= malloc( sizeof( filter_sys_t
) );
233 p_filter
->p_sys
= p_sys
;
235 switch( p_filter
->fmt_in
.video
.i_chroma
)
239 p_sys
->drawingPixelFunction
= drawPixelI420
;
243 p_sys
->drawingPixelFunction
= drawPixelPacked
;
245 GetPackedYuvOffsets( p_filter
->fmt_in
.video
.i_chroma
,
248 &p_sys
->i_v_offset
);
250 case VLC_CODEC_RGB24
:
251 p_sys
->drawingPixelFunction
= drawPixelRGB24
;
255 msg_Err( p_filter
, "Unsupported input chroma (%4.4s)",
256 (char*)&(p_filter
->fmt_in
.video
.i_chroma
) );
260 p_sys
->p_image
= image_HandlerCreate( p_filter
);
261 if( p_sys
->p_image
== NULL
)
264 p_filter
->pf_video_filter
= Filter
;
266 config_ChainParse( p_filter
, FILTER_PREFIX
, ppsz_filter_options
,
270 var_CreateGetNonEmptyStringCommand( p_filter
,
271 FILTER_PREFIX
"color" ) ) )
273 msg_Err( p_filter
, "configuration variable "
274 FILTER_PREFIX
"color empty" );
275 p_sys
->ballColor
= RED
;
278 p_sys
->ballColor
= getBallColor( p_this
, psz_method
);
283 var_CreateGetIntegerCommand( p_filter
, FILTER_PREFIX
"size" );
285 var_CreateGetIntegerCommand( p_filter
, FILTER_PREFIX
"speed" );
286 p_sys
->b_edgeVisible
=
287 var_CreateGetBoolCommand( p_filter
, FILTER_PREFIX
"edge-visible" );
288 p_sys
->i_gradThresh
=
289 var_CreateGetIntegerCommand( p_filter
, FILTER_PREFIX
"gradient-threshold" );
291 vlc_mutex_init( &p_sys
->lock
);
293 var_AddCallback( p_filter
, FILTER_PREFIX
"color",
294 ballCallback
, p_sys
);
295 var_AddCallback( p_filter
, FILTER_PREFIX
"size",
296 ballCallback
, p_sys
);
297 var_AddCallback( p_filter
, FILTER_PREFIX
"speed",
298 ballCallback
, p_sys
);
299 var_AddCallback( p_filter
, FILTER_PREFIX
"edge-visible",
300 ballCallback
, p_sys
);
302 p_sys
->p_smooth
= NULL
;
303 p_sys
->p_grad_x
= NULL
;
304 p_sys
->p_grad_y
= NULL
;
306 p_sys
->i_ball_x
= 100;
307 p_sys
->i_ball_y
= 100;
309 p_sys
->f_lastVect_x
= 0;
310 p_sys
->f_lastVect_y
= -1;
316 /*****************************************************************************
317 * Destroy: destroy Distort video thread output method
318 *****************************************************************************
319 * Terminate an output method created by DistortCreateOutputMethod
320 *****************************************************************************/
321 static void Destroy( vlc_object_t
*p_this
)
323 filter_t
*p_filter
= (filter_t
*)p_this
;
324 filter_sys_t
*p_sys
= p_filter
->p_sys
;
326 var_DelCallback( p_filter
, FILTER_PREFIX
"color",
327 ballCallback
, p_sys
);
328 var_DelCallback( p_filter
, FILTER_PREFIX
"size",
329 ballCallback
, p_sys
);
330 var_DelCallback( p_filter
, FILTER_PREFIX
"speed",
331 ballCallback
, p_sys
);
332 var_DelCallback( p_filter
, FILTER_PREFIX
"edge-visible",
333 ballCallback
, p_sys
);
335 vlc_mutex_destroy( &p_sys
->lock
);
337 image_HandlerDelete( p_sys
->p_image
);
339 free( p_sys
->p_smooth
);
340 free( p_sys
->p_grad_x
);
341 free( p_sys
->p_grad_y
);
347 /*****************************************************************************
348 * Render: displays previously rendered output
349 *****************************************************************************
350 * This function send the currently rendered image to Distort image, waits
351 * until it is displayed and switch the two rendering buffers, preparing next
353 *****************************************************************************/
354 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
358 if( !p_pic
) return NULL
;
360 p_outpic
= filter_NewPicture( p_filter
);
363 picture_Release( p_pic
);
367 filter_sys_t
*p_sys
= p_filter
->p_sys
;
368 vlc_mutex_lock( &p_sys
->lock
);
369 FilterBall( p_filter
, p_pic
, p_outpic
);
370 vlc_mutex_unlock( &p_sys
->lock
);
372 return CopyInfoAndRelease( p_outpic
, p_pic
);
376 /*****************************************************************************
378 *****************************************************************************/
380 static void drawBall( filter_sys_t
*p_sys
, picture_t
*p_outpic
)
382 int x
= p_sys
->i_ball_x
;
383 int y
= p_sys
->i_ball_y
;
384 int size
= p_sys
->i_ballSize
;
386 const int i_width
= p_outpic
->p
[0].i_visible_pitch
;
387 const int i_height
= p_outpic
->p
[0].i_visible_lines
;
389 for( int j
= y
- size
; j
<= y
+ size
; j
++ )
391 bool b_skip
= ( x
- size
) % 2;
392 for( int i
= x
- size
; i
<= x
+ size
; i
++ )
394 /* Draw the pixel if it is inside the disk
395 and check we don't write out the frame. */
396 if( ( i
- x
) * ( i
- x
) + ( j
- y
) * ( j
- y
) <= size
* size
397 && i
>= 0 && i
< i_width
398 && j
>= 0 && j
< i_height
)
400 ( *p_sys
->drawingPixelFunction
)( p_sys
, p_outpic
,
401 p_sys
->colorList
[ p_sys
->ballColor
].comp1
,
402 p_sys
->colorList
[ p_sys
->ballColor
].comp2
,
403 p_sys
->colorList
[ p_sys
->ballColor
].comp3
,
412 static void drawPixelRGB24( filter_sys_t
*p_sys
, picture_t
*p_outpic
,
413 uint8_t R
, uint8_t G
, uint8_t B
,
414 int x
, int y
, bool b_skip
)
417 VLC_UNUSED( b_skip
);
418 uint8_t *p_pixel
= p_outpic
->p
[0].p_pixels
419 + p_outpic
->p
[0].i_pitch
427 static void drawPixelI420( filter_sys_t
*p_sys
, picture_t
*p_outpic
,
428 uint8_t Y
, uint8_t U
, uint8_t V
,
429 int x
, int y
, bool b_skip
)
432 *( p_outpic
->p
[0].p_pixels
+ p_outpic
->p
[0].i_pitch
* y
+ x
) = Y
;
435 *( p_outpic
->p
[2].p_pixels
+ p_outpic
->p
[2].i_pitch
436 * ( y
/ 2 ) + x
/ 2 ) = U
;
437 *( p_outpic
->p
[1].p_pixels
+ p_outpic
->p
[1].i_pitch
438 * ( y
/ 2 ) + x
/ 2 ) = V
;
443 static void drawPixelPacked( filter_sys_t
*p_sys
, picture_t
*p_outpic
,
444 uint8_t Y
, uint8_t U
, uint8_t V
,
445 int x
, int y
, bool b_skip
)
447 uint8_t *p_pixel
= p_outpic
->p
[0].p_pixels
448 + p_outpic
->p
[0].i_pitch
* y
+ x
* 2;
449 *( p_pixel
+ p_sys
->i_y_offset
) = Y
;
452 *( p_pixel
+ p_sys
->i_u_offset
) = U
;
453 *( p_pixel
+ p_sys
->i_v_offset
) = V
;
458 /*****************************************************************************
460 *****************************************************************************
461 * Modify its value to set its norm to 1 and keep its direction.
462 *****************************************************************************/
463 static void NormalizeVector( float *vect_x
, float *vect_y
)
465 float norm
= sqrt( *vect_x
* *vect_x
+ *vect_y
* *vect_y
);
466 if( *vect_x
!= 0 || *vect_y
!= 0 )
474 /*****************************************************************************
475 * Gaussian Convolution
476 *****************************************************************************
477 * Gaussian convolution ( sigma == 1.4 )
479 * | 2 4 5 4 2 | | 2 4 4 4 2 |
480 * | 4 9 12 9 4 | | 4 8 12 8 4 |
481 * | 5 12 15 12 5 | ~ | 4 12 16 12 4 |
482 * | 4 9 12 9 4 | | 4 8 12 8 4 |
483 * | 2 4 5 4 2 | | 2 4 4 4 2 |
484 *****************************************************************************/
485 static void GaussianConvolution( picture_t
*p_inpic
, uint32_t *p_smooth
)
487 const uint8_t *p_inpix
= p_inpic
->p
[Y_PLANE
].p_pixels
;
488 const int i_src_pitch
= p_inpic
->p
[Y_PLANE
].i_pitch
;
489 const int i_src_visible
= p_inpic
->p
[Y_PLANE
].i_visible_pitch
;
490 const int i_numLines
= p_inpic
->p
[Y_PLANE
].i_visible_lines
;
492 for( int y
= 2; y
< i_numLines
- 2; y
++ )
494 for( int x
= 2; x
< i_src_visible
- 2; x
++ )
496 p_smooth
[y
*i_src_visible
+x
] = (uint32_t)(
498 ( p_inpix
[(y
-2)*i_src_pitch
+x
-2] )
499 + ((p_inpix
[(y
-2)*i_src_pitch
+x
-1]
500 + p_inpix
[(y
-2)*i_src_pitch
+x
]
501 + p_inpix
[(y
-2)*i_src_pitch
+x
+1])<<1 )
502 + ( p_inpix
[(y
-2)*i_src_pitch
+x
+2] )
504 + ((p_inpix
[(y
-1)*i_src_pitch
+x
-2]
505 + ( p_inpix
[(y
-1)*i_src_pitch
+x
-1]<<1 )
506 + ( p_inpix
[(y
-1)*i_src_pitch
+x
]*3 )
507 + ( p_inpix
[(y
-1)*i_src_pitch
+x
+1]<<1 )
508 + p_inpix
[(y
-1)*i_src_pitch
+x
+2]
510 + p_inpix
[y
*i_src_pitch
+x
-2]
511 + ( p_inpix
[y
*i_src_pitch
+x
-1]*3 )
512 + ( p_inpix
[y
*i_src_pitch
+x
]<<2 )
513 + ( p_inpix
[y
*i_src_pitch
+x
+1]*3 )
514 + p_inpix
[y
*i_src_pitch
+x
+2]
516 + p_inpix
[(y
+1)*i_src_pitch
+x
-2]
517 + ( p_inpix
[(y
+1)*i_src_pitch
+x
-1]<<1 )
518 + ( p_inpix
[(y
+1)*i_src_pitch
+x
]*3 )
519 + ( p_inpix
[(y
+1)*i_src_pitch
+x
+1]<<1 )
520 + p_inpix
[(y
+1)*i_src_pitch
+x
+2] )<<1 )
522 + ( p_inpix
[(y
+2)*i_src_pitch
+x
-2] )
523 + ((p_inpix
[(y
+2)*i_src_pitch
+x
-1]
524 + p_inpix
[(y
+2)*i_src_pitch
+x
]
525 + p_inpix
[(y
+2)*i_src_pitch
+x
+1])<<1 )
526 + ( p_inpix
[(y
+2)*i_src_pitch
+x
+2] )
533 /*****************************************************************************
534 * FilterBall: Augmented reality ball video filter
535 *****************************************************************************
536 * The edge detection part comes from gradient.c video filter module.
537 * The Canny edge detection algorithm is used :
538 * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
539 * (well ... the implementation isn't really the canny algorithm ... but some
540 * ideas are the same)
541 *****************************************************************************/
542 static void FilterBall( filter_t
*p_filter
, picture_t
*p_inpic
,
543 picture_t
*p_outpic
)
546 filter_sys_t
*p_sys
= p_filter
->p_sys
;
552 picture_t
*p_converted
;
553 video_format_t fmt_comp
;
554 memset( &fmt_comp
, 0, sizeof(fmt_comp
) );
556 switch( p_filter
->fmt_in
.video
.i_chroma
)
558 case VLC_CODEC_RGB24
:
560 fmt_comp
.i_width
= p_filter
->fmt_in
.video
.i_width
;
561 fmt_comp
.i_height
= p_filter
->fmt_in
.video
.i_height
;
562 fmt_comp
.i_chroma
= VLC_FOURCC('G','R','E','Y');
563 fmt_comp
.i_visible_width
= fmt_comp
.i_width
;
564 fmt_comp
.i_visible_height
= fmt_comp
.i_height
;
566 p_converted
= image_Convert( p_sys
->p_image
, p_inpic
,
567 &(p_filter
->fmt_in
.video
),
575 p_converted
= p_inpic
;
579 const int i_numCols
= p_converted
->p
[0].i_visible_pitch
;
580 const int i_numLines
= p_converted
->p
[0].i_visible_lines
;
582 if( !p_sys
->p_smooth
)
584 (uint32_t *)vlc_alloc( i_numLines
* i_numCols
,
586 p_smooth
= p_sys
->p_smooth
;
588 if( !p_sys
->p_grad_x
)
590 (int32_t *)vlc_alloc( i_numLines
* i_numCols
,
592 p_grad_x
= p_sys
->p_grad_x
;
594 if( !p_sys
->p_grad_y
)
596 (int32_t *)vlc_alloc( i_numLines
* i_numCols
,
598 p_grad_y
= p_sys
->p_grad_y
;
600 if( !p_smooth
|| !p_grad_x
|| !p_grad_y
) return;
602 memcpy( p_outpic
->p
[0].p_pixels
, p_inpic
->p
[0].p_pixels
,
603 p_outpic
->p
[0].i_lines
* p_outpic
->p
[0].i_pitch
);
604 memcpy( p_outpic
->p
[1].p_pixels
, p_inpic
->p
[1].p_pixels
,
605 p_outpic
->p
[1].i_lines
* p_outpic
->p
[1].i_pitch
);
606 memcpy( p_outpic
->p
[2].p_pixels
, p_inpic
->p
[2].p_pixels
,
607 p_outpic
->p
[2].i_lines
* p_outpic
->p
[2].i_pitch
);
609 GaussianConvolution( p_converted
, p_smooth
);
611 /* Compute the picture Sobel gradient
613 | -2 0 2 | and | 0 0 0 |
614 | -1 0 1 | | -1 -2 -1 | */
616 for( int y
= 1; y
< i_numLines
- 1; y
++ )
618 for( int x
= 1; x
< i_numCols
- 1; x
++ )
621 p_grad_x
[ y
* i_numCols
+ x
] =
622 ( p_smooth
[(y
-1)*i_numCols
+x
-1]
623 - p_smooth
[(y
+1)*i_numCols
+x
-1] )
624 + ( ( p_smooth
[(y
-1)*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
+1)*i_numCols
+x
+1] );
628 p_grad_y
[ y
* i_numCols
+ x
] =
629 ( p_smooth
[(y
-1)*i_numCols
+x
-1]
630 - p_smooth
[(y
-1)*i_numCols
+x
+1] )
631 + ( ( p_smooth
[y
*i_numCols
+x
-1]
632 - p_smooth
[y
*i_numCols
+x
+1] ) <<1 )
633 + ( p_smooth
[(y
+1)*i_numCols
+x
-1]
634 - p_smooth
[(y
+1)*i_numCols
+x
+1] );
638 if( p_sys
->b_edgeVisible
)
640 /* Display the edges. */
641 for( int y
= 1; y
< i_numLines
- 1; y
++ )
643 for( int x
= 1; x
< i_numCols
- 1; x
++ )
645 if( abs( p_grad_x
[ y
* i_numCols
+ x
] )
646 + abs( p_grad_y
[ y
* i_numCols
+ x
] )
647 > p_sys
->i_gradThresh
)
649 ( *p_sys
->drawingPixelFunction
)( p_sys
, p_outpic
,
650 p_sys
->colorList
[ WHITE
].comp1
,
651 p_sys
->colorList
[ WHITE
].comp2
,
652 p_sys
->colorList
[ WHITE
].comp3
,
661 float *pf_lastVect_x
= &p_sys
->f_lastVect_x
;
662 float *pf_lastVect_y
= &p_sys
->f_lastVect_y
;
664 float f_newVect_x
= 0;
665 float f_newVect_y
= 0;
666 float f_contVect_x
= 0;
667 float f_contVect_y
= 0;
669 int nb_collisions
= 0;
673 /* Test collisions for each pixel the ball will cover in its
675 for ( i_motion
= 0; i_motion
<= p_sys
->i_ballSpeed
&& !bounce
; i_motion
++ )
677 /* Compute next ball position */
678 x
= roundf( (float)p_sys
->i_ball_x
679 + *pf_lastVect_x
* (float)i_motion
);
680 y
= roundf( (float)p_sys
->i_ball_y
681 + *pf_lastVect_y
* (float)i_motion
);
683 for( int i
= x
- p_sys
->i_ballSize
; i
<= x
+ p_sys
->i_ballSize
; i
++ )
685 for( int j
= y
- p_sys
->i_ballSize
;
686 j
<= y
+ p_sys
->i_ballSize
; j
++ )
688 /* Test the pixel if it is inside the disk and check we don't
689 write out the frame. */
690 if( ( i
- x
) * ( i
- x
) + ( j
- y
) * ( j
- y
)
691 == p_sys
->i_ballSize
* p_sys
->i_ballSize
692 && j
<= i_numLines
- 1 && x
<= i_numCols
- 1
693 && j
>= 0 && i
>= 0 )
695 /* Test firstly the picture limit collisions. */
698 f_contVect_x
= x
- i
;
708 f_contVect_y
= y
- j
;
714 if( j
>= i_numLines
- 3 )
717 f_contVect_y
= y
- j
;
723 if( i
>= i_numCols
- 3 )
725 f_contVect_x
= x
- i
;
732 /* Test the collisions with edges. */
733 if( abs( p_grad_x
[ j
* i_numCols
+ i
] )
734 + abs( p_grad_y
[ j
* i_numCols
+ i
] )
735 > p_sys
->i_gradThresh
)
737 f_contVect_x
+= x
- i
;
738 f_contVect_y
+= y
- j
;
751 /* Compute normal vector. */
752 f_contVect_x
/= nb_collisions
;
753 f_contVect_y
/= nb_collisions
;
754 NormalizeVector( &f_contVect_x
, &f_contVect_y
);
756 /* Compute the new vector after the bounce. */
757 float cosinus
= *pf_lastVect_x
* f_contVect_x
758 + *pf_lastVect_y
* f_contVect_y
;
759 f_newVect_x
= *pf_lastVect_x
- 2 * cosinus
* f_contVect_x
;
760 f_newVect_y
= *pf_lastVect_y
- 2 * cosinus
* f_contVect_y
;
761 NormalizeVector( &f_newVect_x
, &f_newVect_y
);
763 *pf_lastVect_x
= f_newVect_x
;
764 *pf_lastVect_y
= f_newVect_y
;
769 /* Test if next pixel is outside the frame limits.
770 If it is the case, then the ball is blocked until it can move. */
771 x
= roundf( (float)x
+ *pf_lastVect_x
);
772 y
= roundf( (float)y
+ *pf_lastVect_y
);
773 if( x
- p_sys
->i_ballSize
< 2
774 || x
+ p_sys
->i_ballSize
> i_numCols
- 3
775 || y
- p_sys
->i_ballSize
< 2
776 || y
+ p_sys
->i_ballSize
> i_numLines
- 3 )
782 /* After a bouce, the first ball motion is always one pixel. */
783 i_motion
= p_sys
->i_ballSpeed
- 1;
788 /* Compute next ball position. */
789 p_sys
->i_ball_x
= roundf( (float)p_sys
->i_ball_x
+ *pf_lastVect_x
790 * (float)( p_sys
->i_ballSpeed
- i_motion
) );
791 p_sys
->i_ball_y
= roundf( p_sys
->i_ball_y
+ *pf_lastVect_y
792 * (float)( p_sys
->i_ballSpeed
- i_motion
) );
795 drawBall( p_sys
, p_outpic
);
797 switch( p_filter
->fmt_in
.video
.i_chroma
)
799 case VLC_CODEC_RGB24
:
801 picture_Release( p_converted
);
808 /*****************************************************************************
810 *****************************************************************************
811 * filter parameter modification callback
812 *****************************************************************************/
813 static int ballCallback( vlc_object_t
*p_this
, char const *psz_var
,
814 vlc_value_t oldval
, vlc_value_t newval
,
818 filter_sys_t
*p_sys
= (filter_sys_t
*)p_data
;
819 msg_Err( p_this
, "Test" );
821 vlc_mutex_lock( &p_sys
->lock
);
822 if( !strcmp( psz_var
, FILTER_PREFIX
"color" ) )
824 p_sys
->ballColor
= getBallColor( p_this
, newval
.psz_string
);
826 else if( !strcmp( psz_var
, FILTER_PREFIX
"size" ) )
828 p_sys
->i_ballSize
= newval
.i_int
;
830 else if( !strcmp( psz_var
, FILTER_PREFIX
"speed" ) )
832 p_sys
->i_ballSpeed
= newval
.i_int
;
834 else if( !strcmp( psz_var
, FILTER_PREFIX
"edge-visible" ) )
836 p_sys
->b_edgeVisible
= newval
.b_bool
;
838 else if( !strcmp( psz_var
, FILTER_PREFIX
"gradient-threshold" ) )
840 p_sys
->i_gradThresh
= newval
.i_int
;
842 vlc_mutex_unlock( &p_sys
->lock
);
848 /*****************************************************************************
850 *****************************************************************************
851 * Get and assign the ball color value
852 *****************************************************************************/
853 static int getBallColor( vlc_object_t
*p_this
, char const *psz_newval
)
856 if( !strcmp( psz_newval
, "red" ) )
858 else if( !strcmp( psz_newval
, "blue" ) )
860 else if( !strcmp( psz_newval
, "green" ) )
862 else if( !strcmp( psz_newval
, "white" ) )
866 msg_Err( p_this
, "no valid ball color provided (%s)", psz_newval
);