1 /*****************************************************************************
2 * distort.c : Misc video effects plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2006 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Antoine Cellerier <dionoea -at- videolan -dot- org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
31 #include <math.h> /* sin(), cos() */
36 #include "filter_common.h"
37 #include "vlc_image.h"
39 enum { WAVE, RIPPLE, GRADIENT, EDGE, HOUGH, PSYCHEDELIC };
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Create ( vlc_object_t * );
45 static void Destroy ( vlc_object_t * );
47 static int Init ( vout_thread_t * );
48 static void End ( vout_thread_t * );
49 static void Render ( vout_thread_t *, picture_t * );
51 static void DistortWave ( vout_thread_t *, picture_t *, picture_t * );
52 static void DistortRipple ( vout_thread_t *, picture_t *, picture_t * );
53 static void DistortGradient( vout_thread_t *, picture_t *, picture_t * );
54 static void DistortEdge ( vout_thread_t *, picture_t *, picture_t * );
55 static void DistortHough ( vout_thread_t *, picture_t *, picture_t * );
56 static void DistortPsychedelic( vout_thread_t *, picture_t *, picture_t * );
58 static int SendEvents ( vlc_object_t *, char const *,
59 vlc_value_t, vlc_value_t, void * );
61 /*****************************************************************************
63 *****************************************************************************/
64 #define MODE_TEXT N_("Distort mode")
65 #define MODE_LONGTEXT N_("Distort mode, one of \"wave\", \"ripple\", \"gradient\", \"edge\", \"hough\" and \"psychedelic\".")
67 #define GRADIENT_TEXT N_("Gradient image type")
68 #define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1). 0 will " \
69 "turn the image to white while 1 will keep colors." )
71 #define CARTOON_TEXT N_("Apply cartoon effect")
72 #define CARTOON_LONGTEXT N_("Apply cartoon effect. It is only used by " \
73 "\"gradient\" and \"edge\".")
75 static char *mode_list[] = { "wave", "ripple", "gradient", "edge", "hough",
77 static char *mode_list_text[] = { N_("Wave"), N_("Ripple"), N_("Gradient"),
78 N_("Edge"), N_("Hough"), N_("Psychedelic") };
81 set_description( _("Distort video filter") );
82 set_shortname( N_( "Distortion" ));
83 set_capability( "video filter", 0 );
84 set_category( CAT_VIDEO );
85 set_subcategory( SUBCAT_VIDEO_VFILTER );
87 add_string( "distort-mode", "wave", NULL, MODE_TEXT, MODE_LONGTEXT,
89 change_string_list( mode_list, mode_list_text, 0 );
91 add_integer_with_range( "distort-gradient-type", 0, 0, 1, NULL,
92 GRADIENT_TEXT, GRADIENT_LONGTEXT, VLC_FALSE );
93 add_bool( "distort-cartoon", 1, NULL,
94 CARTOON_TEXT, CARTOON_LONGTEXT, VLC_FALSE );
96 add_shortcut( "distort" );
97 set_callbacks( Create, Destroy );
100 /*****************************************************************************
101 * vout_sys_t: Distort video output method descriptor
102 *****************************************************************************
103 * This structure is part of the video output thread descriptor.
104 * It describes the Distort specific properties of an output thread.
105 *****************************************************************************/
109 vout_thread_t *p_vout;
111 /* For the wave mode */
115 /* For the gradient mode */
117 vlc_bool_t b_cartoon;
119 /* For pyschedelic mode */
120 image_handler_t *p_image;
121 unsigned int x, y, scale;
122 int xinc, yinc, scaleinc;
129 /*****************************************************************************
130 * Control: control facility for the vout (forwards to child vout)
131 *****************************************************************************/
132 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
134 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
137 /*****************************************************************************
138 * Create: allocates Distort video thread output method
139 *****************************************************************************
140 * This function allocates and initializes a Distort vout method.
141 *****************************************************************************/
142 static int Create( vlc_object_t *p_this )
144 vout_thread_t *p_vout = (vout_thread_t *)p_this;
145 char *psz_method, *psz_method_tmp;
147 /* Allocate structure */
148 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
149 if( p_vout->p_sys == NULL )
151 msg_Err( p_vout, "out of memory" );
155 p_vout->pf_init = Init;
156 p_vout->pf_end = End;
157 p_vout->pf_manage = NULL;
158 p_vout->pf_render = Render;
159 p_vout->pf_display = NULL;
160 p_vout->pf_control = Control;
162 p_vout->p_sys->i_mode = 0;
163 p_vout->p_sys->p_pre_hough = NULL;
165 if( !(psz_method = psz_method_tmp
166 = config_GetPsz( p_vout, "distort-mode" )) )
168 msg_Err( p_vout, "configuration variable %s empty, using 'wave'",
170 p_vout->p_sys->i_mode = WAVE;
174 if( !strcmp( psz_method, "wave" ) )
176 p_vout->p_sys->i_mode = WAVE;
178 else if( !strcmp( psz_method, "ripple" ) )
180 p_vout->p_sys->i_mode = RIPPLE;
182 else if( !strcmp( psz_method, "gradient" ) )
184 p_vout->p_sys->i_mode = GRADIENT;
186 else if( !strcmp( psz_method, "edge" ) )
188 p_vout->p_sys->i_mode = EDGE;
190 else if( !strcmp( psz_method, "hough" ) )
192 p_vout->p_sys->i_mode = HOUGH;
194 else if( !strcmp( psz_method, "psychedelic" ) )
196 p_vout->p_sys->i_mode = PSYCHEDELIC;
197 p_vout->p_sys->x = 10;
198 p_vout->p_sys->y = 10;
199 p_vout->p_sys->scale = 1;
200 p_vout->p_sys->xinc = 1;
201 p_vout->p_sys->yinc = 1;
202 p_vout->p_sys->scaleinc = 1;
203 p_vout->p_sys->u = 0;
204 p_vout->p_sys->v = 0;
208 msg_Err( p_vout, "no valid distort mode provided, using wave" );
209 p_vout->p_sys->i_mode = WAVE;
212 free( psz_method_tmp );
214 p_vout->p_sys->i_gradient_type =
215 config_GetInt( p_vout, "distort-gradient-type" );
216 p_vout->p_sys->b_cartoon =
217 config_GetInt( p_vout, "distort-cartoon" );
222 /*****************************************************************************
223 * Init: initialize Distort video thread output method
224 *****************************************************************************/
225 static int Init( vout_thread_t *p_vout )
229 video_format_t fmt = {0};
231 I_OUTPUTPICTURES = 0;
233 /* Initialize the output structure */
234 p_vout->output.i_chroma = p_vout->render.i_chroma;
235 p_vout->output.i_width = p_vout->render.i_width;
236 p_vout->output.i_height = p_vout->render.i_height;
237 p_vout->output.i_aspect = p_vout->render.i_aspect;
238 p_vout->fmt_out = p_vout->fmt_in;
239 fmt = p_vout->fmt_out;
241 /* Try to open the real video output */
242 msg_Dbg( p_vout, "spawning the real video output" );
244 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
246 /* Everything failed */
247 if( p_vout->p_sys->p_vout == NULL )
249 msg_Err( p_vout, "cannot open vout, aborting" );
253 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
255 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
257 ADD_PARENT_CALLBACKS( SendEventsToChild );
259 p_vout->p_sys->f_angle = 0.0;
260 p_vout->p_sys->last_date = 0;
262 p_vout->p_sys->p_image = NULL;
267 /*****************************************************************************
268 * End: terminate Distort video thread output method
269 *****************************************************************************/
270 static void End( vout_thread_t *p_vout )
274 /* Free the fake output buffers we allocated */
275 for( i_index = I_OUTPUTPICTURES ; i_index ; )
278 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
282 /*****************************************************************************
283 * Destroy: destroy Distort video thread output method
284 *****************************************************************************
285 * Terminate an output method created by DistortCreateOutputMethod
286 *****************************************************************************/
287 static void Destroy( vlc_object_t *p_this )
289 vout_thread_t *p_vout = (vout_thread_t *)p_this;
291 if( p_vout->p_sys->p_vout )
293 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
294 vlc_object_detach( p_vout->p_sys->p_vout );
295 vout_Destroy( p_vout->p_sys->p_vout );
298 DEL_PARENT_CALLBACKS( SendEventsToChild );
300 if(p_vout->p_sys->p_pre_hough)
301 free(p_vout->p_sys->p_pre_hough);
303 if( p_vout->p_sys->p_image )
304 image_HandlerDelete( p_vout->p_sys->p_image );
306 free( p_vout->p_sys );
309 /*****************************************************************************
310 * Render: displays previously rendered output
311 *****************************************************************************
312 * This function send the currently rendered image to Distort image, waits
313 * until it is displayed and switch the two rendering buffers, preparing next
315 *****************************************************************************/
316 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
320 /* This is a new frame. Get a structure from the video_output. */
321 while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
324 if( p_vout->b_die || p_vout->b_error )
328 msleep( VOUT_OUTMEM_SLEEP );
331 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
333 switch( p_vout->p_sys->i_mode )
336 DistortWave( p_vout, p_pic, p_outpic );
340 DistortRipple( p_vout, p_pic, p_outpic );
344 DistortEdge( p_vout, p_pic, p_outpic );
348 DistortGradient( p_vout, p_pic, p_outpic );
352 DistortHough( p_vout, p_pic, p_outpic );
356 DistortPsychedelic( p_vout, p_pic, p_outpic );
363 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
366 /*****************************************************************************
367 * DistortWave: draw a wave effect on the picture
368 *****************************************************************************/
369 static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
370 picture_t *p_outpic )
374 mtime_t new_date = mdate();
376 p_vout->p_sys->f_angle += (new_date - p_vout->p_sys->last_date) / 200000.0;
377 p_vout->p_sys->last_date = new_date;
378 f_angle = p_vout->p_sys->f_angle;
380 for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
382 int i_line, i_num_lines, i_offset;
384 uint8_t *p_in, *p_out;
386 p_in = p_inpic->p[i_index].p_pixels;
387 p_out = p_outpic->p[i_index].p_pixels;
389 i_num_lines = p_inpic->p[i_index].i_visible_lines;
391 black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
393 /* Ok, we do 3 times the sin() calculation for each line. So what ? */
394 for( i_line = 0 ; i_line < i_num_lines ; i_line++ )
396 /* Calculate today's offset, don't go above 1/20th of the screen */
397 i_offset = (int)( (double)(p_inpic->p[i_index].i_visible_pitch)
398 * sin( f_angle + 10.0 * (double)i_line
399 / (double)i_num_lines )
406 p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
407 p_inpic->p[i_index].i_visible_pitch + i_offset );
408 p_in += p_inpic->p[i_index].i_pitch;
409 p_out += p_outpic->p[i_index].i_pitch;
410 memset( p_out + i_offset, black_pixel, -i_offset );
414 p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
415 p_inpic->p[i_index].i_visible_pitch - i_offset );
416 memset( p_out, black_pixel, i_offset );
417 p_in += p_inpic->p[i_index].i_pitch;
418 p_out += p_outpic->p[i_index].i_pitch;
423 p_vout->p_vlc->pf_memcpy( p_out, p_in,
424 p_inpic->p[i_index].i_visible_pitch );
425 p_in += p_inpic->p[i_index].i_pitch;
426 p_out += p_outpic->p[i_index].i_pitch;
433 /*****************************************************************************
434 * DistortRipple: draw a ripple effect at the bottom of the picture
435 *****************************************************************************/
436 static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
437 picture_t *p_outpic )
441 mtime_t new_date = mdate();
443 p_vout->p_sys->f_angle -= (p_vout->p_sys->last_date - new_date) / 100000.0;
444 p_vout->p_sys->last_date = new_date;
445 f_angle = p_vout->p_sys->f_angle;
447 for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
449 int i_line, i_first_line, i_num_lines, i_offset;
451 uint8_t *p_in, *p_out;
453 black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
455 i_num_lines = p_inpic->p[i_index].i_visible_lines;
457 i_first_line = i_num_lines * 4 / 5;
459 p_in = p_inpic->p[i_index].p_pixels;
460 p_out = p_outpic->p[i_index].p_pixels;
462 for( i_line = 0 ; i_line < i_first_line ; i_line++ )
464 p_vout->p_vlc->pf_memcpy( p_out, p_in,
465 p_inpic->p[i_index].i_visible_pitch );
466 p_in += p_inpic->p[i_index].i_pitch;
467 p_out += p_outpic->p[i_index].i_pitch;
470 /* Ok, we do 3 times the sin() calculation for each line. So what ? */
471 for( i_line = i_first_line ; i_line < i_num_lines ; i_line++ )
473 /* Calculate today's offset, don't go above 1/20th of the screen */
474 i_offset = (int)( (double)(p_inpic->p[i_index].i_pitch)
475 * sin( f_angle + 2.0 * (double)i_line
476 / (double)( 1 + i_line
478 * (double)(i_line - i_first_line)
479 / (double)i_num_lines
486 p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
487 p_inpic->p[i_index].i_visible_pitch + i_offset );
488 p_in -= p_inpic->p[i_index].i_pitch;
489 p_out += p_outpic->p[i_index].i_pitch;
490 memset( p_out + i_offset, black_pixel, -i_offset );
494 p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
495 p_inpic->p[i_index].i_visible_pitch - i_offset );
496 memset( p_out, black_pixel, i_offset );
497 p_in -= p_inpic->p[i_index].i_pitch;
498 p_out += p_outpic->p[i_index].i_pitch;
503 p_vout->p_vlc->pf_memcpy( p_out, p_in,
504 p_inpic->p[i_index].i_visible_pitch );
505 p_in -= p_inpic->p[i_index].i_pitch;
506 p_out += p_outpic->p[i_index].i_pitch;
513 /*****************************************************************************
514 * Gaussian Convolution
515 *****************************************************************************
516 * Gaussian convolution ( sigma == 1.4 )
518 * | 2 4 5 4 2 | | 2 4 4 4 2 |
519 * | 4 9 12 9 4 | | 4 8 12 8 4 |
520 * | 5 12 15 12 5 | ~ | 4 12 16 12 4 |
521 * | 4 9 12 9 4 | | 4 8 12 8 4 |
522 * | 2 4 5 4 2 | | 2 4 4 4 2 |
523 *****************************************************************************/
524 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
526 uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
527 int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
528 int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
529 int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
532 for( y = 2; y < i_num_lines - 2; y++ )
534 for( x = 2; x < i_src_visible - 2; x++ )
536 p_smooth[y*i_src_visible+x] = (uint32_t)(
538 ( p_inpix[(y-2)*i_src_pitch+x-2]<<1 )
539 + ( p_inpix[(y-2)*i_src_pitch+x-1]<<2 )
540 + ( p_inpix[(y-2)*i_src_pitch+x]<<2 )
541 + ( p_inpix[(y-2)*i_src_pitch+x+1]<<2 )
542 + ( p_inpix[(y-2)*i_src_pitch+x+2]<<1 )
544 + ( p_inpix[(y-1)*i_src_pitch+x-2]<<2 )
545 + ( p_inpix[(y-1)*i_src_pitch+x-1]<<3 )
546 + ( p_inpix[(y-1)*i_src_pitch+x]*12 )
547 + ( p_inpix[(y-1)*i_src_pitch+x+1]<<3 )
548 + ( p_inpix[(y-1)*i_src_pitch+x+2]<<2 )
550 + ( p_inpix[y*i_src_pitch+x-2]<<2 )
551 + ( p_inpix[y*i_src_pitch+x-1]*12 )
552 + ( p_inpix[y*i_src_pitch+x]<<4 )
553 + ( p_inpix[y*i_src_pitch+x+1]*12 )
554 + ( p_inpix[y*i_src_pitch+x+2]<<2 )
556 + ( p_inpix[(y+1)*i_src_pitch+x-2]<<2 )
557 + ( p_inpix[(y+1)*i_src_pitch+x-1]<<3 )
558 + ( p_inpix[(y+1)*i_src_pitch+x]*12 )
559 + ( p_inpix[(y+1)*i_src_pitch+x+1]<<3 )
560 + ( p_inpix[(y+1)*i_src_pitch+x+2]<<2 )
562 + ( p_inpix[(y+2)*i_src_pitch+x-2]<<1 )
563 + ( p_inpix[(y+2)*i_src_pitch+x-1]<<2 )
564 + ( p_inpix[(y+2)*i_src_pitch+x]<<2 )
565 + ( p_inpix[(y+2)*i_src_pitch+x+1]<<2 )
566 + ( p_inpix[(y+2)*i_src_pitch+x+2]<<1 )
572 /*****************************************************************************
573 * DistortGradient: Sobel
574 *****************************************************************************/
575 static void DistortGradient( vout_thread_t *p_vout, picture_t *p_inpic,
576 picture_t *p_outpic )
579 int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
580 int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
581 int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
582 int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
584 uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
585 uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
587 uint32_t *p_smooth = (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
589 if( !p_smooth ) return;
591 if( p_vout->p_sys->b_cartoon )
593 p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
594 p_inpic->p[U_PLANE].p_pixels,
595 p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
596 p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
597 p_inpic->p[V_PLANE].p_pixels,
598 p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
602 p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
603 p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
604 p_vout->p_vlc->pf_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
605 p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
608 GaussianConvolution( p_inpic, p_smooth );
613 | -2 0 2 | and | 0 0 0 |
614 | -1 0 1 | | -1 -2 -1 | */
616 for( y = 1; y < i_num_lines - 1; y++ )
618 for( x = 1; x < i_src_visible - 1; x++ )
623 ( ( p_smooth[(y-1)*i_src_visible+x]
624 - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
625 + ( p_smooth[(y-1)*i_src_visible+x-1]
626 - p_smooth[(y+1)*i_src_visible+x-1] )
627 + ( p_smooth[(y-1)*i_src_visible+x+1]
628 - p_smooth[(y+1)*i_src_visible+x+1] )
632 ( ( p_smooth[y*i_src_visible+x-1]
633 - p_smooth[y*i_src_visible+x+1] ) <<1 )
634 + ( p_smooth[(y-1)*i_src_visible+x-1]
635 - p_smooth[(y-1)*i_src_visible+x+1] )
636 + ( p_smooth[(y+1)*i_src_visible+x-1]
637 - p_smooth[(y+1)*i_src_visible+x+1] )
640 if( p_vout->p_sys->i_gradient_type )
642 if( p_vout->p_sys->b_cartoon )
646 p_outpix[y*i_dst_pitch+x] = 0x00;
650 if( p_smooth[y*i_src_visible+x] > 0xa0 )
651 p_outpix[y*i_dst_pitch+x] =
652 0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
653 else if( p_smooth[y*i_src_visible+x] > 0x70 )
654 p_outpix[y*i_dst_pitch+x] =
655 0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
656 else if( p_smooth[y*i_src_visible+x] > 0x28 )
657 p_outpix[y*i_dst_pitch+x] =
658 0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
660 p_outpix[y*i_dst_pitch+x] =
661 0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
667 p_outpix[y*i_dst_pitch+x] = 255;
669 p_outpix[y*i_dst_pitch+x] = (uint8_t)a;
675 p_outpix[y*i_dst_pitch+x] = 0;
677 p_outpix[y*i_dst_pitch+x] = (uint8_t)(255 - a);
682 if( p_smooth ) free( p_smooth );
685 /*****************************************************************************
686 * DistortEdge: Canny edge detection algorithm
687 *****************************************************************************
688 * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
689 * (well ... my implementation isn't really the canny algorithm ... but some
690 * ideas are the same)
691 *****************************************************************************/
700 static void DistortEdge( vout_thread_t *p_vout, picture_t *p_inpic,
701 picture_t *p_outpic )
705 int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
706 int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
707 int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
708 int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
710 uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
711 uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
713 uint32_t *p_smooth = malloc( i_num_lines*i_src_visible * sizeof(uint32_t) );
714 uint32_t *p_grad = malloc( i_num_lines*i_src_visible *sizeof(uint32_t) );
715 uint8_t *p_theta = malloc( i_num_lines*i_src_visible *sizeof(uint8_t) );
717 if( !p_smooth || !p_grad || !p_theta ) return;
719 if( p_vout->p_sys->b_cartoon )
721 p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
722 p_inpic->p[U_PLANE].p_pixels,
723 p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
724 p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
725 p_inpic->p[V_PLANE].p_pixels,
726 p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
730 p_vout->p_vlc->pf_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
731 p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
732 p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
733 p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
734 memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
735 p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
738 GaussianConvolution( p_inpic, p_smooth );
743 | -2 0 2 | and | 0 0 0 |
744 | -1 0 1 | | -1 -2 -1 | */
746 for( y = 1; y < i_num_lines - 1; y++ )
748 for( x = 1; x < i_src_visible - 1; x++ )
752 ( ( p_smooth[(y-1)*i_src_visible+x]
753 - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
754 + ( p_smooth[(y-1)*i_src_visible+x-1]
755 - p_smooth[(y+1)*i_src_visible+x-1] )
756 + ( p_smooth[(y-1)*i_src_visible+x+1]
757 - p_smooth[(y+1)*i_src_visible+x+1] );
759 ( ( p_smooth[y*i_src_visible+x-1]
760 - p_smooth[y*i_src_visible+x+1] ) <<1 )
761 + ( p_smooth[(y-1)*i_src_visible+x-1]
762 - p_smooth[(y-1)*i_src_visible+x+1] )
763 + ( p_smooth[(y+1)*i_src_visible+x-1]
764 - p_smooth[(y+1)*i_src_visible+x+1] );
766 p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
768 /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
769 * tan( 26,565051177 ) = 0.5
770 * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
771 * tan( 63,434948823 ) 2 */
772 if( (grady<<1) > gradx )
773 p_theta[y*i_src_visible+x] = THETA_P;
774 else if( (grady<<1) < -gradx )
775 p_theta[y*i_src_visible+x] = THETA_M;
776 else if( !gradx || abs(grady) > abs(gradx)<<1 )
777 p_theta[y*i_src_visible+x] = THETA_Y;
779 p_theta[y*i_src_visible+x] = THETA_X;
784 for( y = 1; y < i_num_lines - 1; y++ )
786 for( x = 1; x < i_src_visible - 1; x++ )
788 if( p_grad[y*i_src_visible+x] > 40 )
790 switch( p_theta[y*i_src_visible+x] )
793 if( p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
794 && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
796 p_outpix[y*i_dst_pitch+x] = 0;
797 } else goto colorize;
800 if( p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
801 && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
803 p_outpix[y*i_dst_pitch+x] = 0;
804 } else goto colorize;
807 if( p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
808 && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
810 p_outpix[y*i_dst_pitch+x] = 0;
811 } else goto colorize;
814 if( p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
815 && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
817 p_outpix[y*i_dst_pitch+x] = 0;
818 } else goto colorize;
825 if( p_vout->p_sys->b_cartoon )
827 if( p_smooth[y*i_src_visible+x] > 0xa0 )
828 p_outpix[y*i_dst_pitch+x] = (uint8_t)
829 0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
830 else if( p_smooth[y*i_src_visible+x] > 0x70 )
831 p_outpix[y*i_dst_pitch+x] =(uint8_t)
832 0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
833 else if( p_smooth[y*i_src_visible+x] > 0x28 )
834 p_outpix[y*i_dst_pitch+x] =(uint8_t)
835 0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
837 p_outpix[y*i_dst_pitch+x] =(uint8_t)
838 0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
843 if( p_smooth ) free( p_smooth );
844 if( p_grad ) free( p_grad );
845 if( p_theta) free( p_theta );
848 /*****************************************************************************
850 *****************************************************************************/
851 #define p_pre_hough p_vout->p_sys->p_pre_hough
852 static void DistortHough( vout_thread_t *p_vout, picture_t *p_inpic,
853 picture_t *p_outpic )
856 int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
857 int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
858 int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
860 uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
862 int i_diag = sqrt( i_num_lines * i_num_lines +
863 i_src_visible * i_src_visible);
864 int i_max, i_phi_max, i_rho, i_rho_max;
866 double d_step = M_PI / i_nb_steps;
870 int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
871 if( ! p_hough ) return;
872 p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
873 if( !p_smooth ) return;
877 msg_Dbg(p_vout, "Starting precalculation");
878 p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
879 if( ! p_pre_hough ) return;
880 for( i = 0 ; i < i_nb_steps ; i++)
882 d_sin = sin(d_step * i);
883 d_cos = cos(d_step * i);
884 for( y = 0 ; y < i_num_lines ; y++ )
885 for( x = 0 ; x < i_src_visible ; x++ )
887 p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
888 ceil(x*d_sin + y*d_cos);
891 msg_Dbg(p_vout, "Precalculation done");
894 memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
896 p_vout->p_vlc->pf_memcpy(
897 p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
898 p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
899 p_vout->p_vlc->pf_memcpy(
900 p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
901 p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
902 p_vout->p_vlc->pf_memcpy(
903 p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
904 p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
906 GaussianConvolution( p_inpic, p_smooth );
911 | -2 0 2 | and | 0 0 0 |
912 | -1 0 1 | | -1 -2 -1 | */
917 for( y = 4; y < i_num_lines - 4; y++ )
919 for( x = 4; x < i_src_visible - 4; x++ )
924 ( ( p_smooth[(y-1)*i_src_visible+x]
925 - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
926 + ( p_smooth[(y-1)*i_src_visible+x-1]
927 - p_smooth[(y+1)*i_src_visible+x-1] )
928 + ( p_smooth[(y-1)*i_src_visible+x+1]
929 - p_smooth[(y+1)*i_src_visible+x+1] )
933 ( ( p_smooth[y*i_src_visible+x-1]
934 - p_smooth[y*i_src_visible+x+1] ) <<1 )
935 + ( p_smooth[(y-1)*i_src_visible+x-1]
936 - p_smooth[(y-1)*i_src_visible+x+1] )
937 + ( p_smooth[(y+1)*i_src_visible+x-1]
938 - p_smooth[(y+1)*i_src_visible+x+1] )
943 for( i = 0 ; i < i_nb_steps ; i ++ )
945 i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
946 if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
948 i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
957 d_sin = sin(i_phi_max*d_step);
958 d_cos = cos(i_phi_max*d_step);
961 for( x = 0 ; x < i_src_visible ; x++ )
963 y = (i_rho_max - x * d_sin) / d_cos;
964 if( y >= 0 && y < i_num_lines )
965 p_outpix[y*i_dst_pitch+x] = 255;
969 if( p_hough ) free( p_hough );
970 if( p_smooth ) free( p_smooth );
974 /*****************************************************************************
976 *****************************************************************************/
977 static void DistortPsychedelic( vout_thread_t *p_vout, picture_t *p_inpic,
978 picture_t *p_outpic )
984 video_format_t fmt_out = {0};
985 picture_t *p_converted;
987 if( !p_vout->p_sys->p_image )
988 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
991 u = p_vout->p_sys->u;
992 v = p_vout->p_sys->v;
993 for( y = 0; y<p_outpic->p[U_PLANE].i_lines; y++)
995 memset( p_outpic->p[U_PLANE].p_pixels+y*p_outpic->p[U_PLANE].i_pitch,
996 u, p_outpic->p[U_PLANE].i_pitch );
997 memset( p_outpic->p[V_PLANE].p_pixels+y*p_outpic->p[V_PLANE].i_pitch,
998 v, p_outpic->p[V_PLANE].i_pitch );
999 if( v == 0 && u != 0 )
1001 else if( u == 0xff )
1003 else if( v == 0xff )
1010 p_vout->p_vlc->pf_memcpy(
1011 p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
1012 p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
1015 /* image visualization */
1016 fmt_out = p_vout->fmt_out;
1017 fmt_out.i_width = p_vout->render.i_width*p_vout->p_sys->scale/150;
1018 fmt_out.i_height = p_vout->render.i_height*p_vout->p_sys->scale/150;
1019 p_converted = image_Convert( p_vout->p_sys->p_image, p_inpic,
1020 &(p_inpic->format), &fmt_out );
1022 #define copyimage( plane, b ) \
1023 for( y=0; y<p_converted->p[plane].i_visible_lines; y++) { \
1024 for( x=0; x<p_converted->p[plane].i_visible_pitch; x++) { \
1026 if( p_vout->p_sys->yinc == 1 ) \
1029 ny = p_converted->p[plane].i_visible_lines-y; \
1030 if( p_vout->p_sys->xinc == 1 ) \
1033 nx = p_converted->p[plane].i_visible_pitch-x; \
1034 p_outpic->p[plane].p_pixels[(p_vout->p_sys->x*b+nx)+(ny+p_vout->p_sys->y*b)*p_outpic->p[plane].i_pitch ] = p_converted->p[plane].p_pixels[y*p_converted->p[plane].i_pitch+x]; \
1036 copyimage( Y_PLANE, 2 );
1037 copyimage( U_PLANE, 1 );
1038 copyimage( V_PLANE, 1 );
1041 p_converted->pf_release( p_converted );
1043 p_vout->p_sys->x += p_vout->p_sys->xinc;
1044 p_vout->p_sys->y += p_vout->p_sys->yinc;
1046 p_vout->p_sys->scale += p_vout->p_sys->scaleinc;
1047 if( p_vout->p_sys->scale >= 50 ) p_vout->p_sys->scaleinc = -1;
1048 if( p_vout->p_sys->scale <= 1 ) p_vout->p_sys->scaleinc = 1;
1050 w = p_vout->render.i_width*p_vout->p_sys->scale/150;
1051 h = p_vout->render.i_height*p_vout->p_sys->scale/150;
1052 if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
1053 p_vout->p_sys->xinc = -1;
1054 if( p_vout->p_sys->x <= 0 )
1055 p_vout->p_sys->xinc = 1;
1057 if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
1058 p_vout->p_sys->x = (p_vout->render.i_width-w)/2;
1059 if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
1060 p_vout->p_sys->y = (p_vout->render.i_height-h)/2;
1062 if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
1063 p_vout->p_sys->yinc = -1;
1064 if( p_vout->p_sys->y <= 0 )
1065 p_vout->p_sys->yinc = 1;
1067 for( y = 0; y< 16; y++ )
1069 if( p_vout->p_sys->v == 0 && p_vout->p_sys->u != 0 )
1070 p_vout->p_sys->u -= 1;
1071 else if( p_vout->p_sys->u == 0xff )
1072 p_vout->p_sys->v -= 1;
1073 else if( p_vout->p_sys->v == 0xff )
1074 p_vout->p_sys->u += 1;
1075 else if( p_vout->p_sys->u == 0 )
1076 p_vout->p_sys->v += 1;
1081 /*****************************************************************************
1082 * SendEvents: forward mouse and keyboard events to the parent p_vout
1083 *****************************************************************************/
1084 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
1085 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1087 var_Set( (vlc_object_t *)p_data, psz_var, newval );
1092 /*****************************************************************************
1093 * SendEventsToChild: forward events to the child/children vout
1094 *****************************************************************************/
1095 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
1096 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1098 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1099 var_Set( p_vout->p_sys->p_vout, psz_var, newval );