]> git.sesse.net Git - vlc/blob - modules/video_filter/distort.c
* modules/gui/wxwidgets/playlist.cpp: don't forget to unlock the playlist.
[vlc] / modules / video_filter / distort.c
1 /*****************************************************************************
2  * distort.c : Misc video effects plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Antoine Cellerier <dionoea -at- videolan -dot- org>
9  *
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.
14  *
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.
19  *
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <math.h>                                            /* sin(), cos() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35
36 #include "filter_common.h"
37
38 #define DISTORT_MODE_WAVE     1
39 #define DISTORT_MODE_RIPPLE   2
40 #define DISTORT_MODE_GRADIENT 3
41 #define DISTORT_MODE_EDGE     4
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static int  Create    ( vlc_object_t * );
47 static void Destroy   ( vlc_object_t * );
48
49 static int  Init      ( vout_thread_t * );
50 static void End       ( vout_thread_t * );
51 static void Render    ( vout_thread_t *, picture_t * );
52
53 static void DistortWave    ( vout_thread_t *, picture_t *, picture_t * );
54 static void DistortRipple  ( vout_thread_t *, picture_t *, picture_t * );
55 static void DistortGradient( vout_thread_t *, picture_t *, picture_t * );
56 static void DistortEdge    ( vout_thread_t *, picture_t *, picture_t * );
57
58 static int  SendEvents   ( vlc_object_t *, char const *,
59                            vlc_value_t, vlc_value_t, void * );
60
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64 #define MODE_TEXT N_("Distort mode")
65 #define MODE_LONGTEXT N_("Distort mode, one of \"wave\", \"ripple\", \"gradient\" and \"edge\"")
66
67 #define GRADIENT_TEXT N_("Gradient image type")
68 #define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1)")
69
70 #define CARTOON_TEXT N_("Apply cartoon effect")
71 #define CARTOON_LONGTEXT N_("Apply cartoon effect. Used by \"gradient\" and \"edge\".")
72
73 static char *mode_list[] = { "wave", "ripple", "gradient", "edge" };
74 static char *mode_list_text[] = { N_("Wave"), N_("Ripple"), N_("gradient"),  N_("Edge") };
75
76 vlc_module_begin();
77     set_description( _("Distort video filter") );
78     set_shortname( N_( "Distortion" ));
79     set_capability( "video filter", 0 );
80     set_category( CAT_VIDEO );
81     set_subcategory( SUBCAT_VIDEO_VFILTER );
82
83     add_string( "distort-mode", "wave", NULL, MODE_TEXT, MODE_LONGTEXT,
84                 VLC_FALSE );
85         change_string_list( mode_list, mode_list_text, 0 );
86
87     add_integer_with_range( "distort-gradient-type", 0, 0, 1, NULL,
88                 GRADIENT_TEXT, GRADIENT_LONGTEXT, VLC_FALSE );
89     add_bool( "distort-cartoon", 1, NULL,
90                 CARTOON_TEXT, CARTOON_LONGTEXT, VLC_FALSE );
91
92     add_shortcut( "distort" );
93     set_callbacks( Create, Destroy );
94 vlc_module_end();
95
96 /*****************************************************************************
97  * vout_sys_t: Distort video output method descriptor
98  *****************************************************************************
99  * This structure is part of the video output thread descriptor.
100  * It describes the Distort specific properties of an output thread.
101  *****************************************************************************/
102 struct vout_sys_t
103 {
104     int i_mode;
105     vout_thread_t *p_vout;
106
107     /* For the wave mode */
108     double  f_angle;
109     mtime_t last_date;
110
111     /* For the gradient mode */
112     int i_gradient_type;
113     vlc_bool_t b_cartoon;
114 };
115
116 /*****************************************************************************
117  * Control: control facility for the vout (forwards to child vout)
118  *****************************************************************************/
119 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
120 {
121     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
122 }
123
124 /*****************************************************************************
125  * Create: allocates Distort video thread output method
126  *****************************************************************************
127  * This function allocates and initializes a Distort vout method.
128  *****************************************************************************/
129 static int Create( vlc_object_t *p_this )
130 {
131     vout_thread_t *p_vout = (vout_thread_t *)p_this;
132     char *psz_method, *psz_method_tmp;
133
134     /* Allocate structure */
135     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
136     if( p_vout->p_sys == NULL )
137     {
138         msg_Err( p_vout, "out of memory" );
139         return VLC_ENOMEM;
140     }
141
142     p_vout->pf_init = Init;
143     p_vout->pf_end = End;
144     p_vout->pf_manage = NULL;
145     p_vout->pf_render = Render;
146     p_vout->pf_display = NULL;
147     p_vout->pf_control = Control;
148
149     p_vout->p_sys->i_mode = 0;
150
151     if( !(psz_method = psz_method_tmp
152           = config_GetPsz( p_vout, "distort-mode" )) )
153     {
154         msg_Err( p_vout, "configuration variable %s empty, using 'wave'",
155                          "distort-mode" );
156         p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
157     }
158     else
159     {
160
161         if( !strcmp( psz_method, "wave" ) )
162         {
163             p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
164         }
165         else if( !strcmp( psz_method, "ripple" ) )
166         {
167             p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
168         }
169         else if( !strcmp( psz_method, "gradient" ) )
170         {
171             p_vout->p_sys->i_mode = DISTORT_MODE_GRADIENT;
172         }
173         else if( !strcmp( psz_method, "edge" ) )
174         {
175             p_vout->p_sys->i_mode = DISTORT_MODE_EDGE;
176         }
177         else
178         {
179             msg_Err( p_vout, "no valid distort mode provided, "
180                              "using wave" );
181             p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
182         }
183     }
184     free( psz_method_tmp );
185
186     p_vout->p_sys->i_gradient_type =
187         config_GetInt( p_vout, "distort-gradient-type" );
188     p_vout->p_sys->b_cartoon =
189         config_GetInt( p_vout, "distort-cartoon" );
190
191     return VLC_SUCCESS;
192 }
193
194 /*****************************************************************************
195  * Init: initialize Distort video thread output method
196  *****************************************************************************/
197 static int Init( vout_thread_t *p_vout )
198 {
199     int i_index;
200     picture_t *p_pic;
201     video_format_t fmt = {0};
202
203     I_OUTPUTPICTURES = 0;
204
205     /* Initialize the output structure */
206     p_vout->output.i_chroma = p_vout->render.i_chroma;
207     p_vout->output.i_width  = p_vout->render.i_width;
208     p_vout->output.i_height = p_vout->render.i_height;
209     p_vout->output.i_aspect = p_vout->render.i_aspect;
210     p_vout->fmt_out = p_vout->fmt_in;
211     fmt = p_vout->fmt_out;
212
213     /* Try to open the real video output */
214     msg_Dbg( p_vout, "spawning the real video output" );
215
216     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
217
218     /* Everything failed */
219     if( p_vout->p_sys->p_vout == NULL )
220     {
221         msg_Err( p_vout, "cannot open vout, aborting" );
222         return VLC_EGENERIC;
223     }
224
225     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
226
227     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
228
229     ADD_PARENT_CALLBACKS( SendEventsToChild );
230
231     p_vout->p_sys->f_angle = 0.0;
232     p_vout->p_sys->last_date = 0;
233
234     return VLC_SUCCESS;
235 }
236
237 /*****************************************************************************
238  * End: terminate Distort video thread output method
239  *****************************************************************************/
240 static void End( vout_thread_t *p_vout )
241 {
242     int i_index;
243
244     /* Free the fake output buffers we allocated */
245     for( i_index = I_OUTPUTPICTURES ; i_index ; )
246     {
247         i_index--;
248         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
249     }
250 }
251
252 /*****************************************************************************
253  * Destroy: destroy Distort video thread output method
254  *****************************************************************************
255  * Terminate an output method created by DistortCreateOutputMethod
256  *****************************************************************************/
257 static void Destroy( vlc_object_t *p_this )
258 {
259     vout_thread_t *p_vout = (vout_thread_t *)p_this;
260
261     if( p_vout->p_sys->p_vout )
262     {
263         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
264         vlc_object_detach( p_vout->p_sys->p_vout );
265         vout_Destroy( p_vout->p_sys->p_vout );
266     }
267
268     DEL_PARENT_CALLBACKS( SendEventsToChild );
269
270     free( p_vout->p_sys );
271 }
272
273 /*****************************************************************************
274  * Render: displays previously rendered output
275  *****************************************************************************
276  * This function send the currently rendered image to Distort image, waits
277  * until it is displayed and switch the two rendering buffers, preparing next
278  * frame.
279  *****************************************************************************/
280 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
281 {
282     picture_t *p_outpic;
283
284     /* This is a new frame. Get a structure from the video_output. */
285     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
286               == NULL )
287     {
288         if( p_vout->b_die || p_vout->b_error )
289         {
290             return;
291         }
292         msleep( VOUT_OUTMEM_SLEEP );
293     }
294
295     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
296
297     switch( p_vout->p_sys->i_mode )
298     {
299         case DISTORT_MODE_WAVE:
300             DistortWave( p_vout, p_pic, p_outpic );
301             break;
302
303         case DISTORT_MODE_RIPPLE:
304             DistortRipple( p_vout, p_pic, p_outpic );
305             break;
306
307         case DISTORT_MODE_EDGE:
308             DistortEdge( p_vout, p_pic, p_outpic );
309             break;
310
311         case DISTORT_MODE_GRADIENT:
312             DistortGradient( p_vout, p_pic, p_outpic );
313             break;
314
315         default:
316             break;
317     }
318
319     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
320 }
321
322 /*****************************************************************************
323  * DistortWave: draw a wave effect on the picture
324  *****************************************************************************/
325 static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
326                                                 picture_t *p_outpic )
327 {
328     int i_index;
329     double f_angle;
330     mtime_t new_date = mdate();
331
332     p_vout->p_sys->f_angle += (new_date - p_vout->p_sys->last_date) / 200000.0;
333     p_vout->p_sys->last_date = new_date;
334     f_angle = p_vout->p_sys->f_angle;
335
336     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
337     {
338         int i_line, i_num_lines, i_offset;
339         uint8_t black_pixel;
340         uint8_t *p_in, *p_out;
341
342         p_in = p_inpic->p[i_index].p_pixels;
343         p_out = p_outpic->p[i_index].p_pixels;
344
345         i_num_lines = p_inpic->p[i_index].i_visible_lines;
346
347         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
348
349         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
350         for( i_line = 0 ; i_line < i_num_lines ; i_line++ )
351         {
352             /* Calculate today's offset, don't go above 1/20th of the screen */
353             i_offset = (int)( (double)(p_inpic->p[i_index].i_visible_pitch)
354                          * sin( f_angle + 10.0 * (double)i_line
355                                                / (double)i_num_lines )
356                          / 20.0 );
357
358             if( i_offset )
359             {
360                 if( i_offset < 0 )
361                 {
362                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
363                              p_inpic->p[i_index].i_visible_pitch + i_offset );
364                     p_in += p_inpic->p[i_index].i_pitch;
365                     p_out += p_outpic->p[i_index].i_pitch;
366                     memset( p_out + i_offset, black_pixel, -i_offset );
367                 }
368                 else
369                 {
370                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
371                              p_inpic->p[i_index].i_visible_pitch - i_offset );
372                     memset( p_out, black_pixel, i_offset );
373                     p_in += p_inpic->p[i_index].i_pitch;
374                     p_out += p_outpic->p[i_index].i_pitch;
375                 }
376             }
377             else
378             {
379                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
380                                           p_inpic->p[i_index].i_visible_pitch );
381                 p_in += p_inpic->p[i_index].i_pitch;
382                 p_out += p_outpic->p[i_index].i_pitch;
383             }
384
385         }
386     }
387 }
388
389 /*****************************************************************************
390  * DistortRipple: draw a ripple effect at the bottom of the picture
391  *****************************************************************************/
392 static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
393                                                   picture_t *p_outpic )
394 {
395     int i_index;
396     double f_angle;
397     mtime_t new_date = mdate();
398
399     p_vout->p_sys->f_angle -= (p_vout->p_sys->last_date - new_date) / 100000.0;
400     p_vout->p_sys->last_date = new_date;
401     f_angle = p_vout->p_sys->f_angle;
402
403     for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ )
404     {
405         int i_line, i_first_line, i_num_lines, i_offset;
406         uint8_t black_pixel;
407         uint8_t *p_in, *p_out;
408
409         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
410
411         i_num_lines = p_inpic->p[i_index].i_visible_lines;
412
413         i_first_line = i_num_lines * 4 / 5;
414
415         p_in = p_inpic->p[i_index].p_pixels;
416         p_out = p_outpic->p[i_index].p_pixels;
417
418         for( i_line = 0 ; i_line < i_first_line ; i_line++ )
419         {
420             p_vout->p_vlc->pf_memcpy( p_out, p_in,
421                                       p_inpic->p[i_index].i_visible_pitch );
422             p_in += p_inpic->p[i_index].i_pitch;
423             p_out += p_outpic->p[i_index].i_pitch;
424         }
425
426         /* Ok, we do 3 times the sin() calculation for each line. So what ? */
427         for( i_line = i_first_line ; i_line < i_num_lines ; i_line++ )
428         {
429             /* Calculate today's offset, don't go above 1/20th of the screen */
430             i_offset = (int)( (double)(p_inpic->p[i_index].i_pitch)
431                          * sin( f_angle + 2.0 * (double)i_line
432                                               / (double)( 1 + i_line
433                                                             - i_first_line) )
434                          * (double)(i_line - i_first_line)
435                          / (double)i_num_lines
436                          / 8.0 );
437
438             if( i_offset )
439             {
440                 if( i_offset < 0 )
441                 {
442                     p_vout->p_vlc->pf_memcpy( p_out, p_in - i_offset,
443                              p_inpic->p[i_index].i_visible_pitch + i_offset );
444                     p_in -= p_inpic->p[i_index].i_pitch;
445                     p_out += p_outpic->p[i_index].i_pitch;
446                     memset( p_out + i_offset, black_pixel, -i_offset );
447                 }
448                 else
449                 {
450                     p_vout->p_vlc->pf_memcpy( p_out + i_offset, p_in,
451                              p_inpic->p[i_index].i_visible_pitch - i_offset );
452                     memset( p_out, black_pixel, i_offset );
453                     p_in -= p_inpic->p[i_index].i_pitch;
454                     p_out += p_outpic->p[i_index].i_pitch;
455                 }
456             }
457             else
458             {
459                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
460                                           p_inpic->p[i_index].i_visible_pitch );
461                 p_in -= p_inpic->p[i_index].i_pitch;
462                 p_out += p_outpic->p[i_index].i_pitch;
463             }
464
465         }
466     }
467 }
468
469 /*****************************************************************************
470  * DistortGradient: Sobel
471  *****************************************************************************/
472 static void DistortGradient( vout_thread_t *p_vout, picture_t *p_inpic,
473                                                   picture_t *p_outpic )
474 {
475     int x, y;
476     int i_height = p_inpic->format.i_height;
477     int i_width = p_inpic->format.i_width;
478
479     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
480     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
481
482     uint32_t p_smooth[ i_height * i_width ];
483
484     if( p_vout->p_sys->b_cartoon )
485     {
486         memcpy( p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
487             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
488         memcpy( p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
489             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
490     }
491     else
492     {
493         memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
494             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
495         memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
496             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
497     }
498
499     /* Gaussian convolution ( sigma == 1.4 )
500
501      |  2  4  5  4  2  |   |  2  4  4  4  2 |
502      |  4  9 12  9  4  |   |  4  8 12  8  4 |
503      |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
504      |  4  9 12  9  4  |   |  4  8 12  8  4 |
505      |  2  4  5  4  2  |   |  2  4  4  4  2 | */
506
507     for( y = 2; y < i_height - 2; y++ )
508     {
509         for( x = 2; x < i_width - 2; x++ )
510         {
511             p_smooth[y*i_width+x] = (
512               /* 2 rows up */
513                 ( p_inpix[(y-2)*i_width+x-2]<<1 )
514               + ( p_inpix[(y-2)*i_width+x-1]<<2 )
515               + ( p_inpix[(y-2)*i_width+x]<<2 )
516               + ( p_inpix[(y-2)*i_width+x+1]<<2 )
517               + ( p_inpix[(y-2)*i_width+x+2]<<1 )
518               /* 1 row up */
519               + ( p_inpix[(y-1)*i_width+x-1]<<3 )
520               + ( p_inpix[(y-1)*i_width+x-2]<<2 )
521               + ( p_inpix[(y-1)*i_width+x]*12 )
522               + ( p_inpix[(y-1)*i_width+x+1]<<3 )
523               + ( p_inpix[(y-1)*i_width+x+2]<<2 )
524               /* */
525               + ( p_inpix[y*i_width+x-2]<<2 )
526               + ( p_inpix[y*i_width+x-1]*12 )
527               + ( p_inpix[y*i_width+x]<<4 )
528               + ( p_inpix[y*i_width+x+1]*12 )
529               + ( p_inpix[y*i_width+x+2]<<2 )
530               /* 1 row down */
531               + ( p_inpix[(y+1)*i_width+x-2]<<2 )
532               + ( p_inpix[(y+1)*i_width+x-1]<<3 )
533               + ( p_inpix[(y+1)*i_width+x]*12 )
534               + ( p_inpix[(y+1)*i_width+x+1]<<3 )
535               + ( p_inpix[(y+1)*i_width+x+2]<<2 )
536               /* 2 rows down */
537               + ( p_inpix[(y+2)*i_width+x-2]<<1 )
538               + ( p_inpix[(y+2)*i_width+x-1]<<2 )
539               + ( p_inpix[(y+2)*i_width+x]<<2 )
540               + ( p_inpix[(y+2)*i_width+x+1]<<2 )
541               + ( p_inpix[(y+2)*i_width+x+2]<<1 )
542               ) >> 7 /* 115 */;
543         }
544     }
545
546     /* Sobel gradient
547
548      | -1 0 1 |     |  1  2  1 |
549      | -2 0 2 | and |  0  0  0 |
550      | -1 0 1 |     | -1 -2 -1 | */
551
552     for( y = 1; y < i_height - 1; y++ )
553     {
554         for( x = 1; x < i_width - 1; x++ )
555         {
556             uint32_t a =
557             (
558               abs(
559                 ((p_smooth[(y-1)*i_width+x] - p_smooth[(y+1)*i_width+x])<<1)
560                + (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y+1)*i_width+x-1])
561                + (p_smooth[(y-1)*i_width+x+1] - p_smooth[(y+1)*i_width+x+1])
562               )
563             +
564               abs(
565                 ((p_smooth[y*i_width+x-1] - p_smooth[y*i_width+x+1])<<1)
566                + (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y-1)*i_width+x+1])
567                + (p_smooth[(y+1)*i_width+x-1] - p_smooth[(y+1)*i_width+x+1])
568               )
569             );
570             if( p_vout->p_sys->i_gradient_type )
571             {
572                 if( p_vout->p_sys->b_cartoon )
573                 {
574                     if( a > 60 )
575                     {
576                         p_outpix[y*i_width+x] = 0x00;
577                     }
578                     else
579                     {
580                         if( p_smooth[y*i_width+x] > 0xa0 )
581                             p_outpix[y*i_width+x] =
582                                 0xff - ((0xff - p_inpix[y*i_width+x] )>>2);
583                         else if( p_smooth[y*i_width+x] > 0x70 )
584                             p_outpix[y*i_width+x] =
585                                 0xa0 - ((0xa0 - p_inpix[y*i_width+x] )>>2);
586                         else if( p_smooth[y*i_width+x] > 0x28 )
587                             p_outpix[y*i_width+x] =
588                                 0x70 - ((0x70 - p_inpix[y*i_width+x] )>>2);
589                         else
590                             p_outpix[y*i_width+x] =
591                                 0x28 - ((0x28 - p_inpix[y*i_width+x] )>>2);
592                     }
593                 }
594                 else
595                 {
596                     if( a>>8 )
597                         p_outpix[y*i_width+x] = 255;
598                     else
599                         p_outpix[y*i_width+x] = (uint8_t)a;
600                 }
601             }
602             else
603             {
604                 if( a>>8 )
605                     p_outpix[y*i_width+x] = 0;
606                 else
607                     p_outpix[y*i_width+x] = (uint8_t)(255 - a);
608             }
609         }
610     }
611 }
612
613 /*****************************************************************************
614  * DistortEdge: Canny edge detection algorithm
615  *****************************************************************************
616  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
617  * (well ... my implementation isn't really the canny algorithm ... but some
618  * ideas are the same)
619  *****************************************************************************/
620 /* angle : | */
621 #define THETA_Y 0
622 /* angle : - */
623 #define THETA_X 1
624 /* angle : / */
625 #define THETA_P 2
626 /* angle : \ */
627 #define THETA_M 3
628 static void DistortEdge( vout_thread_t *p_vout, picture_t *p_inpic,
629                                                   picture_t *p_outpic )
630 {
631     int x, y;
632
633     int i_height = p_inpic->format.i_height;
634     int i_width = p_inpic->format.i_width;
635
636     uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
637     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
638
639     uint32_t p_smooth[ i_height * i_width ];
640     uint32_t p_grad[ i_height * i_width ];
641     uint8_t p_theta[ i_height * i_width ];
642
643     if( p_vout->p_sys->b_cartoon )
644     {
645         memcpy( p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
646             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
647         memcpy( p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
648             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
649     }
650     else
651     {
652         memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
653               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
654         memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
655             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
656         memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
657             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
658     }
659
660     /* Gaussian convolution ( sigma == 1.4 )
661
662      |  2  4  5  4  2  |   |  2  4  4  4  2 |
663      |  4  9 12  9  4  |   |  4  8 12  8  4 |
664      |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
665      |  4  9 12  9  4  |   |  4  8 12  8  4 |
666      |  2  4  5  4  2  |   |  2  4  4  4  2 | */
667
668     for( y = 2; y < i_height - 2; y++ )
669     {
670         for( x = 2; x < i_width - 2; x++ )
671         {
672             p_smooth[y*i_width+x] = (
673               /* 2 rows up */
674                 ( p_inpix[(y-2)*i_width+x-2]<<1 )
675               + ( p_inpix[(y-2)*i_width+x-1]<<2 )
676               + ( p_inpix[(y-2)*i_width+x]<<2 )
677               + ( p_inpix[(y-2)*i_width+x+1]<<2 )
678               + ( p_inpix[(y-2)*i_width+x+2]<<1 )
679               /* 1 row up */
680               + ( p_inpix[(y-1)*i_width+x-1]<<3 )
681               + ( p_inpix[(y-1)*i_width+x-2]<<2 )
682               + ( p_inpix[(y-1)*i_width+x]*12 )
683               + ( p_inpix[(y-1)*i_width+x+1]<<3 )
684               + ( p_inpix[(y-1)*i_width+x+2]<<2 )
685               /* */
686               + ( p_inpix[y*i_width+x-2]<<2 )
687               + ( p_inpix[y*i_width+x-1]*12 )
688               + ( p_inpix[y*i_width+x]<<4 )
689               + ( p_inpix[y*i_width+x+1]*12 )
690               + ( p_inpix[y*i_width+x+2]<<2 )
691               /* 1 row down */
692               + ( p_inpix[(y+1)*i_width+x-2]<<2 )
693               + ( p_inpix[(y+1)*i_width+x-1]<<3 )
694               + ( p_inpix[(y+1)*i_width+x]*12 )
695               + ( p_inpix[(y+1)*i_width+x+1]<<3 )
696               + ( p_inpix[(y+1)*i_width+x+2]<<2 )
697               /* 2 rows down */
698               + ( p_inpix[(y+2)*i_width+x-2]<<1 )
699               + ( p_inpix[(y+2)*i_width+x-1]<<2 )
700               + ( p_inpix[(y+2)*i_width+x]<<2 )
701               + ( p_inpix[(y+2)*i_width+x+1]<<2 )
702               + ( p_inpix[(y+2)*i_width+x+2]<<1 )
703               ) >> 7 /* 115 */;
704         }
705     }
706
707     /* Sobel gradient
708
709      | -1 0 1 |     |  1  2  1 |
710      | -2 0 2 | and |  0  0  0 |
711      | -1 0 1 |     | -1 -2 -1 | */
712
713     for( y = 1; y < i_height - 1; y++ )
714     {
715         for( x = 1; x < i_width - 1; x++ )
716         {
717             int gradx =
718                 ((p_smooth[(y-1)*i_width+x] - p_smooth[(y+1)*i_width+x])<<1)
719                + (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y+1)*i_width+x-1])
720                + (p_smooth[(y-1)*i_width+x+1] - p_smooth[(y+1)*i_width+x+1]);
721             int grady =
722                 ((p_smooth[y*i_width+x-1] - p_smooth[y*i_width+x+1])<<1)
723                + (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y-1)*i_width+x+1])
724                + (p_smooth[(y+1)*i_width+x-1] - p_smooth[(y+1)*i_width+x+1]);
725             p_grad[y*i_width+x] = abs( gradx ) + abs( grady );
726             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
727              * tan( 26,565051177 ) = 0.5
728              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
729              * tan( 63,434948823 ) 2 */
730             if( (grady<<1) > gradx )
731                 p_theta[y*i_width+x] = THETA_P;
732             else if( (grady<<1) < -gradx )
733                 p_theta[y*i_width+x] = THETA_M;
734             else if( !gradx || abs(grady) > abs(gradx)<<1 )
735                 p_theta[y*i_width+x] = THETA_Y;
736             else
737                 p_theta[y*i_width+x] = THETA_X;
738         }
739     }
740
741     /* edge computing */
742     for( y = 1; y < i_height - 1; y++ )
743     {
744         for( x = 1; x < i_width - 1; x++ )
745         {
746             if( p_grad[y*i_width+x] > 40 )
747             {
748                 switch( p_theta[y*i_width+x] )
749                 {
750                     case THETA_Y:
751                         if(    p_grad[y*i_width+x] > p_grad[(y-1)*i_width+x]
752                             && p_grad[y*i_width+x] > p_grad[(y+1)*i_width+x] )
753                         {
754                             p_outpix[y*i_width+x] = 0;
755                         } else goto colorize;
756                         break;
757                     case THETA_P:
758                         if(    p_grad[y*i_width+x] > p_grad[(y-1)*i_width+x-1]
759                             && p_grad[y*i_width+x] > p_grad[(y+1)*i_width+x+1] )
760                         {
761                             p_outpix[y*i_width+x] = 0;
762                         } else goto colorize;
763                         break;
764                     case THETA_M:
765                         if(    p_grad[y*i_width+x] > p_grad[(y-1)*i_width+x+1]
766                             && p_grad[y*i_width+x] > p_grad[(y+1)*i_width+x-1] )
767                         {
768                             p_outpix[y*i_width+x] = 0;
769                         } else goto colorize;
770                         break;
771                     case THETA_X:
772                         if(    p_grad[y*i_width+x] > p_grad[y*i_width+x-1]
773                             && p_grad[y*i_width+x] > p_grad[y*i_width+x+1] )
774                         {
775                             p_outpix[y*i_width+x] = 0;
776                         } else goto colorize;
777                         break;
778                 }
779             }
780             else
781             {
782                 colorize:
783                 if( p_vout->p_sys->b_cartoon )
784                 {
785                     if( p_smooth[y*i_width+x] > 0xa0 )
786                         p_outpix[y*i_width+x] =
787                             0xff - ((0xff - p_inpix[y*i_width+x] )>>2);
788                     else if( p_smooth[y*i_width+x] > 0x70 )
789                         p_outpix[y*i_width+x] =
790                             0xa0 - ((0xa0 - p_inpix[y*i_width+x] )>>2);
791                     else if( p_smooth[y*i_width+x] > 0x28 )
792                         p_outpix[y*i_width+x] =
793                             0x70 - ((0x70 - p_inpix[y*i_width+x] )>>2);
794                     else
795                         p_outpix[y*i_width+x] =
796                             0x28 - ((0x28 - p_inpix[y*i_width+x] )>>2);
797                 }
798             }
799         }
800     }
801 }
802 /*****************************************************************************
803  * SendEvents: forward mouse and keyboard events to the parent p_vout
804  *****************************************************************************/
805 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
806                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
807 {
808     var_Set( (vlc_object_t *)p_data, psz_var, newval );
809
810     return VLC_SUCCESS;
811 }
812
813 /*****************************************************************************
814  * SendEventsToChild: forward events to the child/children vout
815  *****************************************************************************/
816 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
817                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
818 {
819     vout_thread_t *p_vout = (vout_thread_t *)p_this;
820     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
821     return VLC_SUCCESS;
822 }