]> git.sesse.net Git - vlc/blobdiff - modules/video_filter/distort.c
Video filters and outputs strings (Refs:#438)
[vlc] / modules / video_filter / distort.c
index f4d8958e08a97c33fa0ba5e0356fb922f7f2565b..ad1d564f5b5cb685122963cc6f47dc43effbff02 100644 (file)
@@ -1,10 +1,11 @@
 /*****************************************************************************
  * distort.c : Misc video effects plugin for vlc
  *****************************************************************************
- * Copyright (C) 2000, 2001, 2002, 2003 VideoLAN
- * $Id: distort.c,v 1.11 2003/10/15 22:49:48 gbazin Exp $
+ * Copyright (C) 2000-2006 the VideoLAN team
+ * $Id$
  *
  * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Antoine Cellerier <dionoea -at- videolan -dot- org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +19,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
@@ -33,9 +34,9 @@
 #include <vlc/vout.h>
 
 #include "filter_common.h"
+#include "vlc_image.h"
 
-#define DISTORT_MODE_WAVE    1
-#define DISTORT_MODE_RIPPLE  2
+enum { WAVE, RIPPLE, GRADIENT, EDGE, HOUGH, PSYCHEDELIC };
 
 /*****************************************************************************
  * Local prototypes
@@ -49,6 +50,10 @@ static void Render    ( vout_thread_t *, picture_t * );
 
 static void DistortWave    ( vout_thread_t *, picture_t *, picture_t * );
 static void DistortRipple  ( vout_thread_t *, picture_t *, picture_t * );
+static void DistortGradient( vout_thread_t *, picture_t *, picture_t * );
+static void DistortEdge    ( vout_thread_t *, picture_t *, picture_t * );
+static void DistortHough   ( vout_thread_t *, picture_t *, picture_t * );
+static void DistortPsychedelic( vout_thread_t *, picture_t *, picture_t * );
 
 static int  SendEvents   ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
@@ -57,16 +62,37 @@ static int  SendEvents   ( vlc_object_t *, char const *,
  * Module descriptor
  *****************************************************************************/
 #define MODE_TEXT N_("Distort mode")
-#define MODE_LONGTEXT N_("Distort mode, one of \"wave\" and \"ripple\"")
+#define MODE_LONGTEXT N_("Distort mode, one of \"wave\", \"ripple\", \"gradient\", \"edge\", \"hough\" and \"psychedelic\"")
 
-static char *mode_list[] = { "wave", "ripple", NULL };
+#define GRADIENT_TEXT N_("Gradient image type")
+#define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1). 0 will " \
+        "turn the image to white while 1 will keep colors." )
+
+#define CARTOON_TEXT N_("Apply cartoon effect")
+#define CARTOON_LONGTEXT N_("Apply cartoon effect. Used by \"gradient\" " \
+                            "and \"edge\".")
+
+static char *mode_list[] = { "wave", "ripple", "gradient", "edge", "hough",
+                             "psychedelic" };
+static char *mode_list_text[] = { N_("Wave"), N_("Ripple"), N_("Gradient"),
+                                  N_("Edge"), N_("Hough"), N_("Psychedelic") };
 
 vlc_module_begin();
-    add_category_hint( N_("Distort"), NULL, VLC_FALSE );
-    add_string_from_list( "distort-mode", "wave", mode_list, NULL,
-                          MODE_TEXT, MODE_LONGTEXT, VLC_FALSE );
-    set_description( _("miscellaneous distort video effects filter") );
+    set_description( _("Distort video filter") );
+    set_shortname( N_( "Distortion" ));
     set_capability( "video filter", 0 );
+    set_category( CAT_VIDEO );
+    set_subcategory( SUBCAT_VIDEO_VFILTER );
+
+    add_string( "distort-mode", "wave", NULL, MODE_TEXT, MODE_LONGTEXT,
+                VLC_FALSE );
+        change_string_list( mode_list, mode_list_text, 0 );
+
+    add_integer_with_range( "distort-gradient-type", 0, 0, 1, NULL,
+                GRADIENT_TEXT, GRADIENT_LONGTEXT, VLC_FALSE );
+    add_bool( "distort-cartoon", 1, NULL,
+                CARTOON_TEXT, CARTOON_LONGTEXT, VLC_FALSE );
+
     add_shortcut( "distort" );
     set_callbacks( Create, Destroy );
 vlc_module_end();
@@ -85,8 +111,29 @@ struct vout_sys_t
     /* For the wave mode */
     double  f_angle;
     mtime_t last_date;
+
+    /* For the gradient mode */
+    int i_gradient_type;
+    vlc_bool_t b_cartoon;
+
+    /* For pyschedelic mode */
+    image_handler_t *p_image;
+    unsigned int x, y, scale;
+    int xinc, yinc, scaleinc;
+    uint8_t u,v;
+
+    /* For hough mode */
+    int *p_pre_hough;
 };
 
+/*****************************************************************************
+ * Control: control facility for the vout (forwards to child vout)
+ *****************************************************************************/
+static int Control( vout_thread_t *p_vout, int i_query, va_list args )
+{
+    return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
+}
+
 /*****************************************************************************
  * Create: allocates Distort video thread output method
  *****************************************************************************
@@ -110,59 +157,66 @@ static int Create( vlc_object_t *p_this )
     p_vout->pf_manage = NULL;
     p_vout->pf_render = Render;
     p_vout->pf_display = NULL;
+    p_vout->pf_control = Control;
 
     p_vout->p_sys->i_mode = 0;
-    /* Look what method was requested from command line*/
-    if( !(psz_method = psz_method_tmp = config_GetPsz( p_vout, "filter" )) )
-    {
-        msg_Err( p_vout, "configuration variable %s empty", "filter" );
-        return VLC_EGENERIC;
-    }
-    while( *psz_method && *psz_method != ':' )
-    {
-        psz_method++;
-    }
+    p_vout->p_sys->p_pre_hough = NULL;
 
-    if( !strcmp( psz_method, ":wave" ) )
+    if( !(psz_method = psz_method_tmp
+          = config_GetPsz( p_vout, "distort-mode" )) )
     {
-        p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
+        msg_Err( p_vout, "configuration variable %s empty, using 'wave'",
+                         "distort-mode" );
+        p_vout->p_sys->i_mode = WAVE;
     }
-    else if( !strcmp( psz_method, ":ripple" ) )
+    else
     {
-        p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
-    }
-    free( psz_method_tmp );
-    if( !p_vout->p_sys->i_mode )
-    {
-        /* No method given in commandline. Look what method was
-         requested in configuration system */
-        if( !(psz_method = psz_method_tmp
-              = config_GetPsz( p_vout, "distort-mode" )) )
+        if( !strcmp( psz_method, "wave" ) )
         {
-            msg_Err( p_vout, "configuration variable %s empty, using 'wave'",
-                             "distort-mode" );
-            p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
+            p_vout->p_sys->i_mode = WAVE;
         }
-        else {
-
-            if( !strcmp( psz_method, "wave" ) )
-            {
-                p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
-            }
-            else if( !strcmp( psz_method, "ripple" ) )
-            {
-                p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
-            }
-            else
-            {
-                msg_Err( p_vout, "no valid distort mode provided, "
-                                 "using wave" );
-                p_vout->p_sys->i_mode = DISTORT_MODE_WAVE;
-            }
+        else if( !strcmp( psz_method, "ripple" ) )
+        {
+            p_vout->p_sys->i_mode = RIPPLE;
+        }
+        else if( !strcmp( psz_method, "gradient" ) )
+        {
+            p_vout->p_sys->i_mode = GRADIENT;
+        }
+        else if( !strcmp( psz_method, "edge" ) )
+        {
+            p_vout->p_sys->i_mode = EDGE;
+        }
+        else if( !strcmp( psz_method, "hough" ) )
+        {
+            p_vout->p_sys->i_mode = HOUGH;
+        }
+        else if( !strcmp( psz_method, "psychedelic" ) )
+        {
+            p_vout->p_sys->i_mode = PSYCHEDELIC;
+            p_vout->p_sys->x = 10;
+            p_vout->p_sys->y = 10;
+            p_vout->p_sys->scale = 1;
+            p_vout->p_sys->xinc = 1;
+            p_vout->p_sys->yinc = 1;
+            p_vout->p_sys->scaleinc = 1;
+            p_vout->p_sys->u = 0;
+            p_vout->p_sys->v = 0;
+        }
+        else
+        {
+            msg_Err( p_vout, "no valid distort mode provided, "
+                             "using wave" );
+            p_vout->p_sys->i_mode = WAVE;
         }
     }
     free( psz_method_tmp );
 
+    p_vout->p_sys->i_gradient_type =
+        config_GetInt( p_vout, "distort-gradient-type" );
+    p_vout->p_sys->b_cartoon =
+        config_GetInt( p_vout, "distort-cartoon" );
+
     return VLC_SUCCESS;
 }
 
@@ -173,6 +227,7 @@ static int Init( vout_thread_t *p_vout )
 {
     int i_index;
     picture_t *p_pic;
+    video_format_t fmt = {0};
 
     I_OUTPUTPICTURES = 0;
 
@@ -181,19 +236,18 @@ static int Init( vout_thread_t *p_vout )
     p_vout->output.i_width  = p_vout->render.i_width;
     p_vout->output.i_height = p_vout->render.i_height;
     p_vout->output.i_aspect = p_vout->render.i_aspect;
+    p_vout->fmt_out = p_vout->fmt_in;
+    fmt = p_vout->fmt_out;
 
     /* Try to open the real video output */
     msg_Dbg( p_vout, "spawning the real video output" );
 
-    p_vout->p_sys->p_vout = vout_Create( p_vout,
-                           p_vout->render.i_width, p_vout->render.i_height,
-                           p_vout->render.i_chroma, p_vout->render.i_aspect );
+    p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
 
     /* Everything failed */
     if( p_vout->p_sys->p_vout == NULL )
     {
         msg_Err( p_vout, "cannot open vout, aborting" );
-
         return VLC_EGENERIC;
     }
 
@@ -206,6 +260,8 @@ static int Init( vout_thread_t *p_vout )
     p_vout->p_sys->f_angle = 0.0;
     p_vout->p_sys->last_date = 0;
 
+    p_vout->p_sys->p_image = NULL;
+
     return VLC_SUCCESS;
 }
 
@@ -233,12 +289,21 @@ static void Destroy( vlc_object_t *p_this )
 {
     vout_thread_t *p_vout = (vout_thread_t *)p_this;
 
-    DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
-    vlc_object_detach( p_vout->p_sys->p_vout );
-    vout_Destroy( p_vout->p_sys->p_vout );
+    if( p_vout->p_sys->p_vout )
+    {
+        DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
+        vlc_object_detach( p_vout->p_sys->p_vout );
+        vout_Destroy( p_vout->p_sys->p_vout );
+    }
 
     DEL_PARENT_CALLBACKS( SendEventsToChild );
 
+    if(p_vout->p_sys->p_pre_hough)
+        free(p_vout->p_sys->p_pre_hough);
+
+    if( p_vout->p_sys->p_image )
+        image_HandlerDelete( p_vout->p_sys->p_image );
+
     free( p_vout->p_sys );
 }
 
@@ -268,14 +333,30 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic )
 
     switch( p_vout->p_sys->i_mode )
     {
-        case DISTORT_MODE_WAVE:
+        case WAVE:
             DistortWave( p_vout, p_pic, p_outpic );
             break;
 
-        case DISTORT_MODE_RIPPLE:
+        case RIPPLE:
             DistortRipple( p_vout, p_pic, p_outpic );
             break;
 
+        case EDGE:
+            DistortEdge( p_vout, p_pic, p_outpic );
+            break;
+
+        case GRADIENT:
+            DistortGradient( p_vout, p_pic, p_outpic );
+            break;
+
+        case HOUGH:
+            DistortHough( p_vout, p_pic, p_outpic );
+            break;
+
+        case PSYCHEDELIC:
+            DistortPsychedelic( p_vout, p_pic, p_outpic );
+            break;
+
         default:
             break;
     }
@@ -306,7 +387,7 @@ static void DistortWave( vout_thread_t *p_vout, picture_t *p_inpic,
         p_in = p_inpic->p[i_index].p_pixels;
         p_out = p_outpic->p[i_index].p_pixels;
 
-        i_num_lines = p_inpic->p[i_index].i_lines;
+        i_num_lines = p_inpic->p[i_index].i_visible_lines;
 
         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
 
@@ -372,7 +453,7 @@ static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
 
         black_pixel = ( i_index == Y_PLANE ) ? 0x00 : 0x80;
 
-        i_num_lines = p_inpic->p[i_index].i_lines;
+        i_num_lines = p_inpic->p[i_index].i_visible_lines;
 
         i_first_line = i_num_lines * 4 / 5;
 
@@ -430,6 +511,574 @@ static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
     }
 }
 
+/*****************************************************************************
+ * Gaussian Convolution
+ *****************************************************************************
+ *    Gaussian convolution ( sigma == 1.4 )
+ *
+ *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
+ *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
+ *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
+ *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
+ *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
+ *****************************************************************************/
+static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
+{
+    uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
+    int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
+    int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
+    int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
+
+    int x,y;
+    for( y = 2; y < i_num_lines - 2; y++ )
+    {
+        for( x = 2; x < i_src_visible - 2; x++ )
+        {
+            p_smooth[y*i_src_visible+x] = (uint32_t)(
+              /* 2 rows up */
+                ( p_inpix[(y-2)*i_src_pitch+x-2]<<1 )
+              + ( p_inpix[(y-2)*i_src_pitch+x-1]<<2 )
+              + ( p_inpix[(y-2)*i_src_pitch+x]<<2 )
+              + ( p_inpix[(y-2)*i_src_pitch+x+1]<<2 )
+              + ( p_inpix[(y-2)*i_src_pitch+x+2]<<1 )
+              /* 1 row up */
+              + ( p_inpix[(y-1)*i_src_pitch+x-2]<<2 )
+              + ( p_inpix[(y-1)*i_src_pitch+x-1]<<3 )
+              + ( p_inpix[(y-1)*i_src_pitch+x]*12 )
+              + ( p_inpix[(y-1)*i_src_pitch+x+1]<<3 )
+              + ( p_inpix[(y-1)*i_src_pitch+x+2]<<2 )
+              /* */
+              + ( p_inpix[y*i_src_pitch+x-2]<<2 )
+              + ( p_inpix[y*i_src_pitch+x-1]*12 )
+              + ( p_inpix[y*i_src_pitch+x]<<4 )
+              + ( p_inpix[y*i_src_pitch+x+1]*12 )
+              + ( p_inpix[y*i_src_pitch+x+2]<<2 )
+              /* 1 row down */
+              + ( p_inpix[(y+1)*i_src_pitch+x-2]<<2 )
+              + ( p_inpix[(y+1)*i_src_pitch+x-1]<<3 )
+              + ( p_inpix[(y+1)*i_src_pitch+x]*12 )
+              + ( p_inpix[(y+1)*i_src_pitch+x+1]<<3 )
+              + ( p_inpix[(y+1)*i_src_pitch+x+2]<<2 )
+              /* 2 rows down */
+              + ( p_inpix[(y+2)*i_src_pitch+x-2]<<1 )
+              + ( p_inpix[(y+2)*i_src_pitch+x-1]<<2 )
+              + ( p_inpix[(y+2)*i_src_pitch+x]<<2 )
+              + ( p_inpix[(y+2)*i_src_pitch+x+1]<<2 )
+              + ( p_inpix[(y+2)*i_src_pitch+x+2]<<1 )
+              ) >> 7 /* 115 */;
+        }
+    }
+}
+
+/*****************************************************************************
+ * DistortGradient: Sobel
+ *****************************************************************************/
+static void DistortGradient( vout_thread_t *p_vout, picture_t *p_inpic,
+                                                  picture_t *p_outpic )
+{
+    int x, y;
+    int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
+    int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
+    int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
+    int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
+
+    uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
+    uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
+
+    uint32_t *p_smooth = (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
+
+    if( !p_smooth ) return;
+
+    if( p_vout->p_sys->b_cartoon )
+    {
+        p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
+            p_inpic->p[U_PLANE].p_pixels,
+            p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
+        p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
+            p_inpic->p[V_PLANE].p_pixels,
+            p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
+    }
+    else
+    {
+        p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
+            p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
+        p_vout->p_vlc->pf_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
+            p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
+    }
+
+    GaussianConvolution( p_inpic, p_smooth );
+
+    /* Sobel gradient
+
+     | -1 0 1 |     |  1  2  1 |
+     | -2 0 2 | and |  0  0  0 |
+     | -1 0 1 |     | -1 -2 -1 | */
+
+    for( y = 1; y < i_num_lines - 1; y++ )
+    {
+        for( x = 1; x < i_src_visible - 1; x++ )
+        {
+            uint32_t a =
+            (
+              abs(
+                ( ( p_smooth[(y-1)*i_src_visible+x]
+                    - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
+               + ( p_smooth[(y-1)*i_src_visible+x-1]
+                   - p_smooth[(y+1)*i_src_visible+x-1] )
+               + ( p_smooth[(y-1)*i_src_visible+x+1]
+                   - p_smooth[(y+1)*i_src_visible+x+1] )
+              )
+            +
+              abs(
+                ( ( p_smooth[y*i_src_visible+x-1]
+                    - p_smooth[y*i_src_visible+x+1] ) <<1 )
+               + ( p_smooth[(y-1)*i_src_visible+x-1]
+                   - p_smooth[(y-1)*i_src_visible+x+1] )
+               + ( p_smooth[(y+1)*i_src_visible+x-1]
+                   - p_smooth[(y+1)*i_src_visible+x+1] )
+              )
+            );
+            if( p_vout->p_sys->i_gradient_type )
+            {
+                if( p_vout->p_sys->b_cartoon )
+                {
+                    if( a > 60 )
+                    {
+                        p_outpix[y*i_dst_pitch+x] = 0x00;
+                    }
+                    else
+                    {
+                        if( p_smooth[y*i_src_visible+x] > 0xa0 )
+                            p_outpix[y*i_dst_pitch+x] =
+                                0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
+                        else if( p_smooth[y*i_src_visible+x] > 0x70 )
+                            p_outpix[y*i_dst_pitch+x] =
+                                0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
+                        else if( p_smooth[y*i_src_visible+x] > 0x28 )
+                            p_outpix[y*i_dst_pitch+x] =
+                                0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
+                        else
+                            p_outpix[y*i_dst_pitch+x] =
+                                0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
+                    }
+                }
+                else
+                {
+                    if( a>>8 )
+                        p_outpix[y*i_dst_pitch+x] = 255;
+                    else
+                        p_outpix[y*i_dst_pitch+x] = (uint8_t)a;
+                }
+            }
+            else
+            {
+                if( a>>8 )
+                    p_outpix[y*i_dst_pitch+x] = 0;
+                else
+                    p_outpix[y*i_dst_pitch+x] = (uint8_t)(255 - a);
+            }
+        }
+    }
+
+    if( p_smooth ) free( p_smooth );
+}
+
+/*****************************************************************************
+ * DistortEdge: Canny edge detection algorithm
+ *****************************************************************************
+ * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
+ * (well ... my implementation isn't really the canny algorithm ... but some
+ * ideas are the same)
+ *****************************************************************************/
+/* angle : | */
+#define THETA_Y 0
+/* angle : - */
+#define THETA_X 1
+/* angle : / */
+#define THETA_P 2
+/* angle : \ */
+#define THETA_M 3
+static void DistortEdge( vout_thread_t *p_vout, picture_t *p_inpic,
+                                                  picture_t *p_outpic )
+{
+    int x, y;
+
+    int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
+    int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
+    int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
+    int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
+
+    uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
+    uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
+
+    uint32_t *p_smooth = malloc( i_num_lines*i_src_visible * sizeof(uint32_t) );
+    uint32_t *p_grad = malloc( i_num_lines*i_src_visible *sizeof(uint32_t) );
+    uint8_t *p_theta = malloc( i_num_lines*i_src_visible *sizeof(uint8_t) );
+
+    if( !p_smooth || !p_grad || !p_theta ) return;
+
+    if( p_vout->p_sys->b_cartoon )
+    {
+        p_vout->p_vlc->pf_memcpy( p_outpic->p[U_PLANE].p_pixels,
+            p_inpic->p[U_PLANE].p_pixels,
+            p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
+        p_vout->p_vlc->pf_memcpy( p_outpic->p[V_PLANE].p_pixels,
+            p_inpic->p[V_PLANE].p_pixels,
+            p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
+    }
+    else
+    {
+        p_vout->p_vlc->pf_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
+              p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
+        p_vout->p_vlc->pf_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
+            p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
+        memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
+            p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
+    }
+
+    GaussianConvolution( p_inpic, p_smooth );
+
+    /* Sobel gradient
+
+     | -1 0 1 |     |  1  2  1 |
+     | -2 0 2 | and |  0  0  0 |
+     | -1 0 1 |     | -1 -2 -1 | */
+
+    for( y = 1; y < i_num_lines - 1; y++ )
+    {
+        for( x = 1; x < i_src_visible - 1; x++ )
+        {
+
+            int gradx =
+                ( ( p_smooth[(y-1)*i_src_visible+x]
+                    - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
+               + ( p_smooth[(y-1)*i_src_visible+x-1]
+                   - p_smooth[(y+1)*i_src_visible+x-1] )
+               + ( p_smooth[(y-1)*i_src_visible+x+1]
+                   - p_smooth[(y+1)*i_src_visible+x+1] );
+            int grady =
+                ( ( p_smooth[y*i_src_visible+x-1]
+                    - p_smooth[y*i_src_visible+x+1] ) <<1 )
+               + ( p_smooth[(y-1)*i_src_visible+x-1]
+                   - p_smooth[(y-1)*i_src_visible+x+1] )
+               + ( p_smooth[(y+1)*i_src_visible+x-1]
+                   - p_smooth[(y+1)*i_src_visible+x+1] );
+
+            p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
+
+            /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
+             * tan( 26,565051177 ) = 0.5
+             * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
+             * tan( 63,434948823 ) 2 */
+            if( (grady<<1) > gradx )
+                p_theta[y*i_src_visible+x] = THETA_P;
+            else if( (grady<<1) < -gradx )
+                p_theta[y*i_src_visible+x] = THETA_M;
+            else if( !gradx || abs(grady) > abs(gradx)<<1 )
+                p_theta[y*i_src_visible+x] = THETA_Y;
+            else
+                p_theta[y*i_src_visible+x] = THETA_X;
+        }
+    }
+
+    /* edge computing */
+    for( y = 1; y < i_num_lines - 1; y++ )
+    {
+        for( x = 1; x < i_src_visible - 1; x++ )
+        {
+            if( p_grad[y*i_src_visible+x] > 40 )
+            {
+                switch( p_theta[y*i_src_visible+x] )
+                {
+                    case THETA_Y:
+                        if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
+                            && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
+                        {
+                            p_outpix[y*i_dst_pitch+x] = 0;
+                        } else goto colorize;
+                        break;
+                    case THETA_P:
+                        if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
+                            && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
+                        {
+                            p_outpix[y*i_dst_pitch+x] = 0;
+                        } else goto colorize;
+                        break;
+                    case THETA_M:
+                        if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
+                            && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
+                        {
+                            p_outpix[y*i_dst_pitch+x] = 0;
+                        } else goto colorize;
+                        break;
+                    case THETA_X:
+                        if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
+                            && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
+                        {
+                            p_outpix[y*i_dst_pitch+x] = 0;
+                        } else goto colorize;
+                        break;
+                }
+            }
+            else
+            {
+                colorize:
+                if( p_vout->p_sys->b_cartoon )
+                {
+                    if( p_smooth[y*i_src_visible+x] > 0xa0 )
+                        p_outpix[y*i_dst_pitch+x] = (uint8_t)
+                            0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
+                    else if( p_smooth[y*i_src_visible+x] > 0x70 )
+                        p_outpix[y*i_dst_pitch+x] =(uint8_t)
+                            0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
+                    else if( p_smooth[y*i_src_visible+x] > 0x28 )
+                        p_outpix[y*i_dst_pitch+x] =(uint8_t)
+                            0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
+                    else
+                        p_outpix[y*i_dst_pitch+x] =(uint8_t)
+                            0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
+                }
+            }
+        }
+    }
+    if( p_smooth ) free( p_smooth );
+    if( p_grad ) free( p_grad );
+    if( p_theta) free( p_theta );
+}
+
+/*****************************************************************************
+ * DistortHough
+ *****************************************************************************/
+#define p_pre_hough p_vout->p_sys->p_pre_hough
+static void DistortHough( vout_thread_t *p_vout, picture_t *p_inpic,
+                                                  picture_t *p_outpic )
+{
+    int x, y, i;
+    int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
+    int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
+    int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
+
+    uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
+
+    int i_diag = sqrt( i_num_lines * i_num_lines +
+                        i_src_visible * i_src_visible);
+    int i_max, i_phi_max, i_rho, i_rho_max;
+    int i_nb_steps = 90;
+    double d_step = M_PI / i_nb_steps;
+    double d_sin;
+    double d_cos;
+    uint32_t *p_smooth;
+    int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
+    if( ! p_hough ) return;
+    p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
+    if( !p_smooth ) return;
+
+    if( ! p_pre_hough )
+    {
+        msg_Dbg(p_vout, "Starting precalculation");
+        p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
+        if( ! p_pre_hough ) return;
+        for( i = 0 ; i < i_nb_steps ; i++)
+        {
+            d_sin = sin(d_step * i);
+            d_cos = cos(d_step * i);
+            for( y = 0 ; y < i_num_lines ; y++ )
+                for( x = 0 ; x < i_src_visible ; x++ )
+                {
+                    p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
+                        ceil(x*d_sin + y*d_cos);
+                }
+        }
+        msg_Dbg(p_vout, "Precalculation done");
+    }
+
+    memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
+
+    p_vout->p_vlc->pf_memcpy(
+        p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
+        p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
+    p_vout->p_vlc->pf_memcpy(
+        p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
+        p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
+    p_vout->p_vlc->pf_memcpy(
+        p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
+        p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
+
+    GaussianConvolution( p_inpic, p_smooth );
+
+    /* Sobel gradient
+
+     | -1 0 1 |     |  1  2  1 |
+     | -2 0 2 | and |  0  0  0 |
+     | -1 0 1 |     | -1 -2 -1 | */
+
+    i_max = 0;
+    i_rho_max = 0;
+    i_phi_max = 0;
+    for( y = 4; y < i_num_lines - 4; y++ )
+    {
+        for( x = 4; x < i_src_visible - 4; x++ )
+        {
+            uint32_t a =
+            (
+              abs(
+                ( ( p_smooth[(y-1)*i_src_visible+x]
+                    - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
+               + ( p_smooth[(y-1)*i_src_visible+x-1]
+                   - p_smooth[(y+1)*i_src_visible+x-1] )
+               + ( p_smooth[(y-1)*i_src_visible+x+1]
+                   - p_smooth[(y+1)*i_src_visible+x+1] )
+              )
+            +
+              abs(
+                ( ( p_smooth[y*i_src_visible+x-1]
+                    - p_smooth[y*i_src_visible+x+1] ) <<1 )
+               + ( p_smooth[(y-1)*i_src_visible+x-1]
+                   - p_smooth[(y-1)*i_src_visible+x+1] )
+               + ( p_smooth[(y+1)*i_src_visible+x-1]
+                   - p_smooth[(y+1)*i_src_visible+x+1] )
+              )
+            );
+            if( a>>8 )
+            {
+                for( i = 0 ; i < i_nb_steps ; i ++ )
+                {
+                    i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
+                    if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
+                    {
+                        i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
+                        i_rho_max = i_rho;
+                        i_phi_max = i;
+                    }
+                }
+            }
+        }
+    }
+
+    d_sin = sin(i_phi_max*d_step);
+    d_cos = cos(i_phi_max*d_step);
+    if( d_cos != 0 )
+    {
+        for( x = 0 ; x < i_src_visible ; x++ )
+        {
+            y = (i_rho_max - x * d_sin) / d_cos;
+            if( y >= 0 && y < i_num_lines )
+                p_outpix[y*i_dst_pitch+x] = 255;
+        }
+    }
+
+    if( p_hough ) free( p_hough );
+    if( p_smooth ) free( p_smooth );
+}
+#undef p_pre_hough
+
+/*****************************************************************************
+ * DistortPsychedelic
+ *****************************************************************************/
+static void DistortPsychedelic( vout_thread_t *p_vout, picture_t *p_inpic,
+                                                  picture_t *p_outpic )
+{
+    unsigned int w, h;
+    int x,y;
+    uint8_t u,v;
+
+    video_format_t fmt_out = {0};
+    picture_t *p_converted;
+
+    if( !p_vout->p_sys->p_image )
+        p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
+
+    /* chrominance */
+    u = p_vout->p_sys->u;
+    v = p_vout->p_sys->v;
+    for( y = 0; y<p_outpic->p[U_PLANE].i_lines; y++)
+    {
+        memset( p_outpic->p[U_PLANE].p_pixels+y*p_outpic->p[U_PLANE].i_pitch,
+                u, p_outpic->p[U_PLANE].i_pitch );
+        memset( p_outpic->p[V_PLANE].p_pixels+y*p_outpic->p[V_PLANE].i_pitch,
+                v, p_outpic->p[V_PLANE].i_pitch );
+        if( v == 0 && u != 0 )
+            u --;
+        else if( u == 0xff )
+            v --;
+        else if( v == 0xff )
+            u ++;
+        else if( u == 0 )
+            v ++;
+    }
+
+    /* luminance */
+    p_vout->p_vlc->pf_memcpy(
+                p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
+                p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
+
+
+    /* image visualization */
+    fmt_out = p_vout->fmt_out;
+    fmt_out.i_width = p_vout->render.i_width*p_vout->p_sys->scale/150;
+    fmt_out.i_height = p_vout->render.i_height*p_vout->p_sys->scale/150;
+    p_converted = image_Convert( p_vout->p_sys->p_image, p_inpic,
+                                 &(p_inpic->format), &fmt_out );
+
+#define copyimage( plane, b ) \
+    for( y=0; y<p_converted->p[plane].i_visible_lines; y++) { \
+    for( x=0; x<p_converted->p[plane].i_visible_pitch; x++) { \
+        int nx, ny; \
+        if( p_vout->p_sys->yinc == 1 ) \
+            ny= y; \
+        else \
+            ny = p_converted->p[plane].i_visible_lines-y; \
+        if( p_vout->p_sys->xinc == 1 ) \
+            nx = x; \
+        else \
+            nx = p_converted->p[plane].i_visible_pitch-x; \
+        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]; \
+    } }
+    copyimage( Y_PLANE, 2 );
+    copyimage( U_PLANE, 1 );
+    copyimage( V_PLANE, 1 );
+#undef copyimage
+
+    p_converted->pf_release( p_converted );
+
+    p_vout->p_sys->x += p_vout->p_sys->xinc;
+    p_vout->p_sys->y += p_vout->p_sys->yinc;
+
+    p_vout->p_sys->scale += p_vout->p_sys->scaleinc;
+    if( p_vout->p_sys->scale >= 50 ) p_vout->p_sys->scaleinc = -1;
+    if( p_vout->p_sys->scale <= 1 ) p_vout->p_sys->scaleinc = 1;
+
+    w = p_vout->render.i_width*p_vout->p_sys->scale/150;
+    h = p_vout->render.i_height*p_vout->p_sys->scale/150;
+    if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
+        p_vout->p_sys->xinc = -1;
+    if( p_vout->p_sys->x <= 0 )
+        p_vout->p_sys->xinc = 1;
+
+    if( p_vout->p_sys->x*2 + w >= p_vout->render.i_width )
+        p_vout->p_sys->x = (p_vout->render.i_width-w)/2;
+    if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
+        p_vout->p_sys->y = (p_vout->render.i_height-h)/2;
+
+    if( p_vout->p_sys->y*2 + h >= p_vout->render.i_height )
+        p_vout->p_sys->yinc = -1;
+    if( p_vout->p_sys->y <= 0 )
+        p_vout->p_sys->yinc = 1;
+
+    for( y = 0; y< 16; y++ )
+    {
+        if( p_vout->p_sys->v == 0 && p_vout->p_sys->u != 0 )
+            p_vout->p_sys->u -= 1;
+        else if( p_vout->p_sys->u == 0xff )
+            p_vout->p_sys->v -= 1;
+        else if( p_vout->p_sys->v == 0xff )
+            p_vout->p_sys->u += 1;
+        else if( p_vout->p_sys->u == 0 )
+            p_vout->p_sys->v += 1;
+    }
+}
+
+
 /*****************************************************************************
  * SendEvents: forward mouse and keyboard events to the parent p_vout
  *****************************************************************************/