upnp: remove m_server_list_lock, p_server_list is already protected by s_lock
[vlc.git] / modules / video_filter / gradient.c
1 /*****************************************************************************
2  * gradient.c : Gradient and edge detection video effects plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2008 VLC authors and VideoLAN
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 it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <math.h>                                            /* sin(), cos() */
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_sout.h>
38
39 #include <vlc_filter.h>
40 #include "filter_picture.h"
41
42 enum { GRADIENT, EDGE, HOUGH };
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int  Create    ( vlc_object_t * );
48 static void Destroy   ( vlc_object_t * );
49
50 static picture_t *Filter( filter_t *, picture_t * );
51 static int GradientCallback( vlc_object_t *, char const *,
52                              vlc_value_t, vlc_value_t,
53                              void * );
54
55 static void FilterGradient( filter_t *, picture_t *, picture_t * );
56 static void FilterEdge    ( filter_t *, picture_t *, picture_t * );
57 static void FilterHough   ( filter_t *, picture_t *, picture_t * );
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 #define MODE_TEXT N_("Distort mode")
63 #define MODE_LONGTEXT N_("Distort mode, one of \"gradient\", \"edge\" and \"hough\".")
64
65 #define GRADIENT_TEXT N_("Gradient image type")
66 #define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1). 0 will " \
67         "turn the image to white while 1 will keep colors." )
68
69 #define CARTOON_TEXT N_("Apply cartoon effect")
70 #define CARTOON_LONGTEXT N_("Apply cartoon effect. It is only used by " \
71     "\"gradient\" and \"edge\".")
72
73 #define GRADIENT_HELP N_("Apply color gradient or edge detection effects")
74
75 static const char *const mode_list[] = { "gradient", "edge", "hough" };
76 static const char *const mode_list_text[] = { N_("Gradient"), N_("Edge"), N_("Hough") };
77
78 #define FILTER_PREFIX "gradient-"
79
80 vlc_module_begin ()
81     set_description( N_("Gradient video filter") )
82     set_shortname( N_( "Gradient" ))
83     set_help(GRADIENT_HELP)
84     set_capability( "video filter2", 0 )
85     set_category( CAT_VIDEO )
86     set_subcategory( SUBCAT_VIDEO_VFILTER )
87
88     add_string( FILTER_PREFIX "mode", "gradient",
89                 MODE_TEXT, MODE_LONGTEXT, false )
90         change_string_list( mode_list, mode_list_text )
91
92     add_integer_with_range( FILTER_PREFIX "type", 0, 0, 1,
93                 GRADIENT_TEXT, GRADIENT_LONGTEXT, false )
94     add_bool( FILTER_PREFIX "cartoon", true,
95                 CARTOON_TEXT, CARTOON_LONGTEXT, false )
96
97     add_shortcut( "gradient" )
98     set_callbacks( Create, Destroy )
99 vlc_module_end ()
100
101 static const char *const ppsz_filter_options[] = {
102     "mode", "type", "cartoon", NULL
103 };
104
105 /*****************************************************************************
106  * filter_sys_t: Distort video output method descriptor
107  *****************************************************************************
108  * This structure is part of the video output thread descriptor.
109  * It describes the Distort specific properties of an output thread.
110  *****************************************************************************/
111 struct filter_sys_t
112 {
113     vlc_mutex_t lock;
114     int i_mode;
115
116     /* For the gradient mode */
117     int i_gradient_type;
118     bool b_cartoon;
119
120     uint32_t *p_buf32;
121     uint32_t *p_buf32_bis;
122     uint8_t *p_buf8;
123
124     /* For hough mode */
125     int *p_pre_hough;
126 };
127
128 /*****************************************************************************
129  * Create: allocates Distort video thread output method
130  *****************************************************************************
131  * This function allocates and initializes a Distort vout method.
132  *****************************************************************************/
133 static int Create( vlc_object_t *p_this )
134 {
135     filter_t *p_filter = (filter_t *)p_this;
136     char *psz_method;
137
138     switch( p_filter->fmt_in.video.i_chroma )
139     {
140         CASE_PLANAR_YUV
141             break;
142
143         default:
144              msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
145                       (char*)&(p_filter->fmt_in.video.i_chroma) );
146             return VLC_EGENERIC;
147     }
148
149     /* Allocate structure */
150     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
151     if( p_filter->p_sys == NULL )
152         return VLC_ENOMEM;
153
154     p_filter->pf_video_filter = Filter;
155
156     p_filter->p_sys->p_pre_hough = NULL;
157
158     config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
159                    p_filter->p_cfg );
160
161     if( !(psz_method =
162         var_CreateGetNonEmptyStringCommand( p_filter, FILTER_PREFIX "mode" )) )
163     {
164         msg_Err( p_filter, "configuration variable "
165                  FILTER_PREFIX "mode empty" );
166         p_filter->p_sys->i_mode = GRADIENT;
167     }
168     else
169     {
170         if( !strcmp( psz_method, "gradient" ) )
171         {
172             p_filter->p_sys->i_mode = GRADIENT;
173         }
174         else if( !strcmp( psz_method, "edge" ) )
175         {
176             p_filter->p_sys->i_mode = EDGE;
177         }
178         else if( !strcmp( psz_method, "hough" ) )
179         {
180             p_filter->p_sys->i_mode = HOUGH;
181         }
182         else
183         {
184             msg_Err( p_filter, "no valid gradient mode provided (%s)", psz_method );
185             p_filter->p_sys->i_mode = GRADIENT;
186         }
187     }
188     free( psz_method );
189
190     p_filter->p_sys->i_gradient_type =
191         var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX "type" );
192     p_filter->p_sys->b_cartoon =
193         var_CreateGetBoolCommand( p_filter, FILTER_PREFIX "cartoon" );
194
195     vlc_mutex_init( &p_filter->p_sys->lock );
196     var_AddCallback( p_filter, FILTER_PREFIX "mode",
197                      GradientCallback, p_filter->p_sys );
198     var_AddCallback( p_filter, FILTER_PREFIX "type",
199                      GradientCallback, p_filter->p_sys );
200     var_AddCallback( p_filter, FILTER_PREFIX "cartoon",
201                      GradientCallback, p_filter->p_sys );
202
203     p_filter->p_sys->p_buf32 = NULL;
204     p_filter->p_sys->p_buf32_bis = NULL;
205     p_filter->p_sys->p_buf8 = NULL;
206
207     return VLC_SUCCESS;
208 }
209
210 /*****************************************************************************
211  * Destroy: destroy Distort video thread output method
212  *****************************************************************************
213  * Terminate an output method created by DistortCreateOutputMethod
214  *****************************************************************************/
215 static void Destroy( vlc_object_t *p_this )
216 {
217     filter_t *p_filter = (filter_t *)p_this;
218     filter_sys_t *p_sys = p_filter->p_sys;
219
220     var_DelCallback( p_filter, FILTER_PREFIX "mode",
221                      GradientCallback, p_sys );
222     var_DelCallback( p_filter, FILTER_PREFIX "type",
223                      GradientCallback, p_sys );
224     var_DelCallback( p_filter, FILTER_PREFIX "cartoon",
225                      GradientCallback, p_sys );
226     vlc_mutex_destroy( &p_sys->lock );
227
228     free( p_sys->p_buf32 );
229     free( p_sys->p_buf32_bis );
230     free( p_sys->p_buf8 );
231     free( p_sys->p_pre_hough );
232
233     free( p_sys );
234 }
235
236 /*****************************************************************************
237  * Render: displays previously rendered output
238  *****************************************************************************
239  * This function send the currently rendered image to Distort image, waits
240  * until it is displayed and switch the two rendering buffers, preparing next
241  * frame.
242  *****************************************************************************/
243 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
244 {
245     picture_t *p_outpic;
246
247     if( !p_pic ) return NULL;
248
249     p_outpic = filter_NewPicture( p_filter );
250     if( !p_outpic )
251     {
252         picture_Release( p_pic );
253         return NULL;
254     }
255
256     vlc_mutex_lock( &p_filter->p_sys->lock );
257     switch( p_filter->p_sys->i_mode )
258     {
259         case EDGE:
260             FilterEdge( p_filter, p_pic, p_outpic );
261             break;
262
263         case GRADIENT:
264             FilterGradient( p_filter, p_pic, p_outpic );
265             break;
266
267         case HOUGH:
268             FilterHough( p_filter, p_pic, p_outpic );
269             break;
270
271         default:
272             break;
273     }
274     vlc_mutex_unlock( &p_filter->p_sys->lock );
275
276     return CopyInfoAndRelease( p_outpic, p_pic );
277 }
278
279 /*****************************************************************************
280  * Gaussian Convolution
281  *****************************************************************************
282  *    Gaussian convolution ( sigma == 1.4 )
283  *
284  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
285  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
286  *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
287  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
288  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
289  *****************************************************************************/
290 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
291 {
292     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
293     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
294     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
295     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
296
297     for( int y = 2; y < i_num_lines - 2; y++ )
298     {
299         for( int x = 2; x < i_src_visible - 2; x++ )
300         {
301             p_smooth[y*i_src_visible+x] = (uint32_t)(
302               /* 2 rows up */
303                 ( p_inpix[(y-2)*i_src_pitch+x-2] )
304               + ((p_inpix[(y-2)*i_src_pitch+x-1]
305               +   p_inpix[(y-2)*i_src_pitch+x]
306               +   p_inpix[(y-2)*i_src_pitch+x+1])<<1 )
307               + ( p_inpix[(y-2)*i_src_pitch+x+2] )
308               /* 1 row up */
309               + ((p_inpix[(y-1)*i_src_pitch+x-2]
310               + ( p_inpix[(y-1)*i_src_pitch+x-1]<<1 )
311               + ( p_inpix[(y-1)*i_src_pitch+x]*3 )
312               + ( p_inpix[(y-1)*i_src_pitch+x+1]<<1 )
313               +   p_inpix[(y-1)*i_src_pitch+x+2]
314               /* */
315               +   p_inpix[y*i_src_pitch+x-2]
316               + ( p_inpix[y*i_src_pitch+x-1]*3 )
317               + ( p_inpix[y*i_src_pitch+x]<<2 )
318               + ( p_inpix[y*i_src_pitch+x+1]*3 )
319               +   p_inpix[y*i_src_pitch+x+2]
320               /* 1 row down */
321               +   p_inpix[(y+1)*i_src_pitch+x-2]
322               + ( p_inpix[(y+1)*i_src_pitch+x-1]<<1 )
323               + ( p_inpix[(y+1)*i_src_pitch+x]*3 )
324               + ( p_inpix[(y+1)*i_src_pitch+x+1]<<1 )
325               +   p_inpix[(y+1)*i_src_pitch+x+2] )<<1 )
326               /* 2 rows down */
327               + ( p_inpix[(y+2)*i_src_pitch+x-2] )
328               + ((p_inpix[(y+2)*i_src_pitch+x-1]
329               +   p_inpix[(y+2)*i_src_pitch+x]
330               +   p_inpix[(y+2)*i_src_pitch+x+1])<<1 )
331               + ( p_inpix[(y+2)*i_src_pitch+x+2] )
332               ) >> 6 /* 115 */;
333         }
334     }
335 }
336
337 /*****************************************************************************
338  * FilterGradient: Sobel
339  *****************************************************************************/
340 static void FilterGradient( filter_t *p_filter, picture_t *p_inpic,
341                                                 picture_t *p_outpic )
342 {
343     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
344     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
345     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
346     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
347
348     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
349     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
350
351     uint32_t *p_smooth;
352     if( !p_filter->p_sys->p_buf32 )
353         p_filter->p_sys->p_buf32 =
354         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
355     p_smooth = p_filter->p_sys->p_buf32;
356
357     if( !p_smooth ) return;
358
359     if( p_filter->p_sys->b_cartoon )
360     {
361         plane_CopyPixels( &p_outpic->p[U_PLANE], &p_inpic->p[U_PLANE] );
362         plane_CopyPixels( &p_outpic->p[V_PLANE], &p_inpic->p[V_PLANE] );
363     }
364     else
365     {
366         memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
367             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
368         memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
369             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
370     }
371
372     GaussianConvolution( p_inpic, p_smooth );
373
374     /* Sobel gradient
375
376      | -1 0 1 |     |  1  2  1 |
377      | -2 0 2 | and |  0  0  0 |
378      | -1 0 1 |     | -1 -2 -1 | */
379
380 #define FOR                                                     \
381     for( int y = 1; y < i_num_lines - 1; y++ )                  \
382     {                                                           \
383         for( int x = 1; x < i_src_visible - 1; x++ )            \
384         {                                                       \
385             const uint32_t a =                                  \
386             (                                                   \
387               abs(                                              \
388                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
389                    - p_smooth[(y+1)*i_src_visible+x-1] )        \
390                + ( ( p_smooth[(y-1)*i_src_visible+x]            \
391                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )   \
392                + ( p_smooth[(y-1)*i_src_visible+x+1]            \
393                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
394               )                                                 \
395             +                                                   \
396               abs(                                              \
397                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
398                    - p_smooth[(y-1)*i_src_visible+x+1] )        \
399                + ( ( p_smooth[y*i_src_visible+x-1]              \
400                     - p_smooth[y*i_src_visible+x+1] ) <<1 )     \
401                + ( p_smooth[(y+1)*i_src_visible+x-1]            \
402                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
403               )                                                 \
404             );
405     if( p_filter->p_sys->i_gradient_type )
406     {
407         if( p_filter->p_sys->b_cartoon )
408         {
409             FOR
410             if( a > 60 )
411             {
412                 p_outpix[y*i_dst_pitch+x] = 0x00;
413             }
414             else
415             {
416                 if( p_smooth[y*i_src_visible+x] > 0xa0 )
417                     p_outpix[y*i_dst_pitch+x] =
418                         0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
419                 else if( p_smooth[y*i_src_visible+x] > 0x70 )
420                     p_outpix[y*i_dst_pitch+x] =
421                         0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
422                 else if( p_smooth[y*i_src_visible+x] > 0x28 )
423                     p_outpix[y*i_dst_pitch+x] =
424                         0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
425                 else
426                     p_outpix[y*i_dst_pitch+x] =
427                         0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
428             }
429             }}
430         }
431         else
432         {
433             FOR
434             p_outpix[y*i_dst_pitch+x] = clip_uint8_vlc( a );
435             }}
436         }
437     }
438     else
439     {
440         FOR
441         if( a>>8 )
442             p_outpix[y*i_dst_pitch+x] = 0;
443         else
444             p_outpix[y*i_dst_pitch+x] = 0xff-(uint8_t)a;
445         }}
446     }
447 #undef FOR
448 }
449
450 /*****************************************************************************
451  * FilterEdge: Canny edge detection algorithm
452  *****************************************************************************
453  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
454  * (well ... my implementation isn't really the canny algorithm ... but some
455  * ideas are the same)
456  *****************************************************************************/
457 /* angle : | */
458 #define THETA_Y 0
459 /* angle : - */
460 #define THETA_X 1
461 /* angle : / */
462 #define THETA_P 2
463 /* angle : \ */
464 #define THETA_M 3
465 static void FilterEdge( filter_t *p_filter, picture_t *p_inpic,
466                                             picture_t *p_outpic )
467 {
468     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
469     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
470     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
471     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
472
473     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
474     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
475
476     uint32_t *p_smooth;
477     uint32_t *p_grad;
478     uint8_t *p_theta;
479
480     if( !p_filter->p_sys->p_buf32 )
481         p_filter->p_sys->p_buf32 =
482         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
483     p_smooth = p_filter->p_sys->p_buf32;
484
485     if( !p_filter->p_sys->p_buf32_bis )
486         p_filter->p_sys->p_buf32_bis =
487         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
488     p_grad = p_filter->p_sys->p_buf32_bis;
489
490     if( !p_filter->p_sys->p_buf8 )
491         p_filter->p_sys->p_buf8 =
492         (uint8_t *)malloc( i_num_lines * i_src_visible * sizeof(uint8_t));
493     p_theta = p_filter->p_sys->p_buf8;
494
495     if( !p_smooth || !p_grad || !p_theta ) return;
496
497     if( p_filter->p_sys->b_cartoon )
498     {
499         plane_CopyPixels( &p_outpic->p[U_PLANE], &p_inpic->p[U_PLANE] );
500         plane_CopyPixels( &p_outpic->p[V_PLANE], &p_inpic->p[V_PLANE] );
501     }
502     else
503     {
504         memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
505               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
506         memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
507             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
508         memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
509             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
510     }
511
512     GaussianConvolution( p_inpic, p_smooth );
513
514     /* Sobel gradient
515
516      | -1 0 1 |     |  1  2  1 |
517      | -2 0 2 | and |  0  0  0 |
518      | -1 0 1 |     | -1 -2 -1 | */
519
520     for( int y = 1; y < i_num_lines - 1; y++ )
521     {
522         for( int x = 1; x < i_src_visible - 1; x++ )
523         {
524
525             const int gradx =
526                  ( p_smooth[(y-1)*i_src_visible+x-1]
527                    - p_smooth[(y+1)*i_src_visible+x-1] )
528                + ( ( p_smooth[(y-1)*i_src_visible+x]
529                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
530                + ( p_smooth[(y-1)*i_src_visible+x+1]
531                    - p_smooth[(y+1)*i_src_visible+x+1] );
532             const int grady =
533                  ( p_smooth[(y-1)*i_src_visible+x-1]
534                    - p_smooth[(y-1)*i_src_visible+x+1] )
535                + ( ( p_smooth[y*i_src_visible+x-1]
536                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
537                + ( p_smooth[(y+1)*i_src_visible+x-1]
538                    - p_smooth[(y+1)*i_src_visible+x+1] );
539
540             p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
541
542             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
543              * tan( 26,565051177 ) = 0.5
544              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
545              * tan( 63,434948823 ) 2 */
546             if( (grady<<1) > gradx )
547                 p_theta[y*i_src_visible+x] = THETA_P;
548             else if( (grady<<1) < -gradx )
549                 p_theta[y*i_src_visible+x] = THETA_M;
550             else if( !gradx || abs(grady) > abs(gradx)<<1 )
551                 p_theta[y*i_src_visible+x] = THETA_Y;
552             else
553                 p_theta[y*i_src_visible+x] = THETA_X;
554         }
555     }
556
557     /* edge computing */
558     for( int y = 1; y < i_num_lines - 1; y++ )
559     {
560         for( int x = 1; x < i_src_visible - 1; x++ )
561         {
562             if( p_grad[y*i_src_visible+x] > 40 )
563             {
564                 switch( p_theta[y*i_src_visible+x] )
565                 {
566                     case THETA_Y:
567                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
568                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
569                         {
570                             p_outpix[y*i_dst_pitch+x] = 0;
571                             break;
572                         } else goto colorize;
573                     case THETA_P:
574                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
575                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
576                         {
577                             p_outpix[y*i_dst_pitch+x] = 0;
578                             break;
579                         } else goto colorize;
580                     case THETA_M:
581                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
582                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
583                         {
584                             p_outpix[y*i_dst_pitch+x] = 0;
585                             break;
586                         } else goto colorize;
587                     case THETA_X:
588                         if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
589                             && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
590                         {
591                             p_outpix[y*i_dst_pitch+x] = 0;
592                             break;
593                         } else goto colorize;
594                 }
595             }
596             else
597             {
598                 colorize:
599                 if( p_filter->p_sys->b_cartoon )
600                 {
601                     if( p_smooth[y*i_src_visible+x] > 0xa0 )
602                         p_outpix[y*i_dst_pitch+x] = (uint8_t)
603                             0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
604                     else if( p_smooth[y*i_src_visible+x] > 0x70 )
605                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
606                             0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
607                     else if( p_smooth[y*i_src_visible+x] > 0x28 )
608                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
609                             0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
610                     else
611                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
612                             0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
613                 }
614             }
615         }
616     }
617 }
618
619 /*****************************************************************************
620  * FilterHough
621  *****************************************************************************/
622 #define p_pre_hough p_filter->p_sys->p_pre_hough
623 static void FilterHough( filter_t *p_filter, picture_t *p_inpic,
624                                              picture_t *p_outpic )
625 {
626     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
627     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
628     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
629
630     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
631
632     int i_diag = sqrt( i_num_lines * i_num_lines +
633                         i_src_visible * i_src_visible);
634     int i_max, i_phi_max, i_rho, i_rho_max;
635     int i_nb_steps = 90;
636     double d_step = M_PI / i_nb_steps;
637     double d_sin;
638     double d_cos;
639     uint32_t *p_smooth;
640
641     int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
642     if( ! p_hough ) return;
643
644     p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
645     if( !p_smooth )
646     {
647         free( p_hough );
648         return;
649     }
650
651     if( ! p_pre_hough )
652     {
653         msg_Dbg(p_filter, "Starting precalculation");
654         p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
655         if( ! p_pre_hough )
656         {
657             free( p_smooth );
658             free( p_hough );
659             return;
660         }
661         for( int i = 0; i < i_nb_steps; i++)
662         {
663             d_sin = sin(d_step * i);
664             d_cos = cos(d_step * i);
665             for( int y = 0; y < i_num_lines; y++ )
666                 for( int x = 0; x < i_src_visible; x++ )
667                 {
668                     p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
669                         ceil(x*d_sin + y*d_cos);
670                 }
671         }
672         msg_Dbg(p_filter, "Precalculation done");
673     }
674
675     memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
676
677     plane_CopyPixels( &p_outpic->p[Y_PLANE], &p_inpic->p[Y_PLANE] );
678     plane_CopyPixels( &p_outpic->p[U_PLANE], &p_inpic->p[U_PLANE] );
679     plane_CopyPixels( &p_outpic->p[V_PLANE], &p_inpic->p[V_PLANE] );
680
681     GaussianConvolution( p_inpic, p_smooth );
682
683     /* Sobel gradient
684
685      | -1 0 1 |     |  1  2  1 |
686      | -2 0 2 | and |  0  0  0 |
687      | -1 0 1 |     | -1 -2 -1 | */
688
689     i_max = 0;
690     i_rho_max = 0;
691     i_phi_max = 0;
692     for( int y = 4; y < i_num_lines - 4; y++ )
693     {
694         for( int x = 4; x < i_src_visible - 4; x++ )
695         {
696             uint32_t a =
697             (
698               abs(
699                 ( ( p_smooth[(y-1)*i_src_visible+x]
700                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
701                + ( p_smooth[(y-1)*i_src_visible+x-1]
702                    - p_smooth[(y+1)*i_src_visible+x-1] )
703                + ( p_smooth[(y-1)*i_src_visible+x+1]
704                    - p_smooth[(y+1)*i_src_visible+x+1] )
705               )
706             +
707               abs(
708                 ( ( p_smooth[y*i_src_visible+x-1]
709                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
710                + ( p_smooth[(y-1)*i_src_visible+x-1]
711                    - p_smooth[(y-1)*i_src_visible+x+1] )
712                + ( p_smooth[(y+1)*i_src_visible+x-1]
713                    - p_smooth[(y+1)*i_src_visible+x+1] )
714               )
715             );
716             if( a>>8 )
717             {
718                 for( int i = 0; i < i_nb_steps; i++ )
719                 {
720                     i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
721                     if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
722                     {
723                         i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
724                         i_rho_max = i_rho;
725                         i_phi_max = i;
726                     }
727                 }
728             }
729         }
730     }
731
732     d_sin = sin(i_phi_max*d_step);
733     d_cos = cos(i_phi_max*d_step);
734     if( d_cos != 0 )
735     {
736         for( int x = 0; x < i_src_visible; x++ )
737         {
738             int y = (i_rho_max - x * d_sin) / d_cos;
739             if( y >= 0 && y < i_num_lines )
740                 p_outpix[y*i_dst_pitch+x] = 255;
741         }
742     }
743
744     free( p_hough );
745     free( p_smooth );
746 }
747 #undef p_pre_hough
748
749
750 static int GradientCallback( vlc_object_t *p_this, char const *psz_var,
751                              vlc_value_t oldval, vlc_value_t newval,
752                              void *p_data )
753 {
754     VLC_UNUSED(oldval);
755     filter_sys_t *p_sys = (filter_sys_t *)p_data;
756
757     vlc_mutex_lock( &p_sys->lock );
758     if( !strcmp( psz_var, FILTER_PREFIX "mode" ) )
759     {
760         if( !strcmp( newval.psz_string, "gradient" ) )
761         {
762             p_sys->i_mode = GRADIENT;
763         }
764         else if( !strcmp( newval.psz_string, "edge" ) )
765         {
766             p_sys->i_mode = EDGE;
767         }
768         else if( !strcmp( newval.psz_string, "hough" ) )
769         {
770             p_sys->i_mode = HOUGH;
771         }
772         else
773         {
774             msg_Err( p_this, "no valid gradient mode provided (%s)", newval.psz_string );
775             p_sys->i_mode = GRADIENT;
776         }
777     }
778     else if( !strcmp( psz_var, FILTER_PREFIX "type" ) )
779     {
780         p_sys->i_gradient_type = newval.i_int;
781     }
782     else if( !strcmp( psz_var, FILTER_PREFIX "cartoon" ) )
783     {
784         p_sys->b_cartoon = newval.b_bool;
785     }
786     vlc_mutex_unlock( &p_sys->lock );
787
788     return VLC_SUCCESS;
789 }