From c170125857f9412350dce43e26284cfadb8a59ac Mon Sep 17 00:00:00 2001
From: Branko Kokanovic <branko.kokanovic@gmail.com>
Date: Sat, 18 Jul 2009 10:18:15 +0200
Subject: [PATCH] Adding mirror video filter

---
 configure.ac                    |    1 +
 modules/video_filter/Modules.am |    1 +
 modules/video_filter/mirror.c   |  437 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 439 insertions(+), 0 deletions(-)
 create mode 100644 modules/video_filter/mirror.c

diff --git a/configure.ac b/configure.ac
index fa506ef..7df8564 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1209,6 +1209,7 @@ dnl Video filters:
 VLC_ADD_PLUGIN([deinterlace])
 VLC_ADD_PLUGIN([yuvp])
 VLC_ADD_PLUGIN([invert])
+VLC_ADD_PLUGIN([mirror])
 VLC_ADD_PLUGIN([adjust])
 VLC_ADD_PLUGIN([transform])
 VLC_ADD_PLUGIN([wave])
diff --git a/modules/video_filter/Modules.am b/modules/video_filter/Modules.am
index c93245d..4c95f6a 100644
--- a/modules/video_filter/Modules.am
+++ b/modules/video_filter/Modules.am
@@ -2,6 +2,7 @@ SUBDIRS = atmo dynamicoverlay
 SOURCES_mosaic = mosaic.c mosaic.h
 SOURCES_transform = transform.c
 SOURCES_invert = invert.c
+SOURCES_mirror = mirror.c
 SOURCES_adjust = adjust.c
 SOURCES_wall = wall.c
 SOURCES_clone = clone.c
diff --git a/modules/video_filter/mirror.c b/modules/video_filter/mirror.c
new file mode 100644
index 0000000..b2be6f3
--- /dev/null
+++ b/modules/video_filter/mirror.c
@@ -0,0 +1,437 @@
+/*****************************************************************************
+ * mirror.c : Mirror video plugin for vlc
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+
+#include <assert.h>
+#include <vlc_filter.h>
+#include "filter_picture.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  Create      ( vlc_object_t * );
+static void Destroy     ( vlc_object_t * );
+
+static picture_t *Filter( filter_t *, picture_t * );
+static void VerticalMirror( picture_t *, picture_t *, int plane, bool );
+static void HorizontalMirror( picture_t *, picture_t *, int, bool );
+static void PlainVerticalMirror( picture_t *, picture_t *, int plane, bool );
+static void RV24VerticalMirror( picture_t *, picture_t *, int plane, bool );
+
+static const char *const ppsz_filter_options[] = {
+    "split", "direction", NULL
+};
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define ORIENTATION_TEXT N_("Mirror orientation")
+#define ORIENTATION_LONGTEXT N_("Defines orientation of the mirror splitting. \
+    Can be vertical or horizontal" )
+static const int pi_orientation_values[] = { 0, 1 };
+static const char *const ppsz_orientation_descriptions[] = {
+  N_("Vertical"), N_("Horizontal") };
+
+#define DIRECTION_TEXT N_("Direction")
+#define DIRECTION_LONGTEXT N_("Direction of the mirroring" )
+static const int pi_direction_values[] = { 0, 1 };
+static const char *const ppsz_direction_descriptions[] = {
+  N_("Left to right/Top to bottom"), N_("Right to left/Bottom to top") };
+
+#define CFG_PREFIX "mirror-"
+
+vlc_module_begin ()
+    set_description( N_("Mirror video filter") )
+    set_shortname( N_("Mirror video" ))
+    set_help( N_("Splits video in two same parts, like in the mirror") )
+    set_category( CAT_VIDEO )
+    set_subcategory( SUBCAT_VIDEO_VFILTER )
+    set_capability( "video filter2", 0 )
+    add_shortcut( "mirror" )
+    add_integer( CFG_PREFIX "split", 0, NULL, ORIENTATION_TEXT,
+                ORIENTATION_LONGTEXT, false )
+        change_integer_list( pi_orientation_values,
+                            ppsz_orientation_descriptions, NULL )
+    add_integer( CFG_PREFIX "direction", 0, NULL, DIRECTION_TEXT,
+                DIRECTION_LONGTEXT, false )
+        change_integer_list( pi_direction_values, ppsz_direction_descriptions,
+                            NULL )
+    set_callbacks( Create, Destroy )
+vlc_module_end ()
+
+/*****************************************************************************
+ * callback prototypes
+ *****************************************************************************/
+static int FilterCallback( vlc_object_t *, char const *,
+                           vlc_value_t, vlc_value_t, void * );
+
+/*****************************************************************************
+ * filter_sys_t: adjust filter method descriptor
+ *****************************************************************************/
+struct filter_sys_t
+{
+    int i_split;
+    int i_direction;
+    vlc_mutex_t lock;
+};
+
+/*****************************************************************************
+ * Create: allocates Mirror video thread output method
+ *****************************************************************************
+ * This function allocates and initializes a Mirror vout method.
+ *****************************************************************************/
+static int Create( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys;
+
+    switch( p_filter->fmt_in.video.i_chroma )
+    {
+        CASE_PLANAR_YUV_SQUARE
+            break;
+        CASE_PACKED_YUV_422
+            break;
+        case VLC_CODEC_RGB24:
+            break;
+
+        default:
+            msg_Err( p_filter, "Unsupported input chroma (%4s)",
+                     (char*)&(p_filter->fmt_in.video.i_chroma) );
+            return VLC_EGENERIC;
+    }
+
+    if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
+    {
+        msg_Err( p_filter, "Input and output chromas don't match" );
+        return VLC_EGENERIC;
+    }
+    
+    /* Allocate structure */
+    p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
+    if( p_filter->p_sys == NULL )
+        return VLC_ENOMEM;
+
+    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
+                       p_filter->p_cfg );
+    p_sys->i_split = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "split" );
+    p_sys->i_direction = var_CreateGetIntegerCommand( p_filter,
+                                                    CFG_PREFIX "direction" );
+
+    vlc_mutex_init( &p_sys->lock );
+
+    var_AddCallback( p_filter, CFG_PREFIX "split", FilterCallback, NULL );
+    var_AddCallback( p_filter, CFG_PREFIX "direction", FilterCallback, NULL );
+
+    p_filter->pf_video_filter = Filter;
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Destroy: destroy Mirror video thread output method
+ *****************************************************************************
+ * Terminate an output method created by MirrorCreateOutputMethod
+ *****************************************************************************/
+static void Destroy( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+
+    var_DelCallback( p_filter, CFG_PREFIX "split", FilterCallback, NULL );
+    var_DelCallback( p_filter, CFG_PREFIX "direction", FilterCallback, NULL );
+
+    vlc_mutex_destroy( &p_filter->p_sys->lock );
+    free( p_filter->p_sys );
+}
+
+/*****************************************************************************
+ * Render: displays previously rendered output
+ *****************************************************************************
+ * This function send the currently rendered image to Mirror image, waits
+ * until it is displayed and switch the two rendering buffers, preparing next
+ * frame.
+ *****************************************************************************/
+static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+{
+    picture_t *p_outpic;
+    filter_sys_t *p_sys = p_filter->p_sys;
+    bool b_vertical_split, b_left_to_right;
+    int i_index;
+
+    if( !p_pic ) return NULL;
+
+    p_outpic = filter_NewPicture( p_filter );
+    if( !p_outpic )
+    {
+        msg_Warn( p_filter, "can't get output picture" );
+        picture_Release( p_pic );
+        return NULL;
+    }
+
+    b_vertical_split = p_sys->i_split == 0 ? true : false;
+    b_left_to_right = p_sys->i_direction == 0 ? true : false;
+
+    for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
+    {
+        if ( b_vertical_split )
+            VerticalMirror( p_pic, p_outpic, i_index, b_left_to_right );
+        else
+            HorizontalMirror( p_pic, p_outpic, i_index, b_left_to_right );
+    }
+
+    return CopyInfoAndRelease( p_outpic, p_pic );
+}
+
+/*****************************************************************************
+ * VerticalMirror: Mirrors vertically image
+ *****************************************************************************
+ * This function is a simple delegate to concrete function for vertical
+ * mirroring depending on the input format.
+ *****************************************************************************/
+static void VerticalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
+                           bool b_left_to_right )
+{
+    switch( p_pic->format.i_chroma )
+    {
+        CASE_PLANAR_YUV_SQUARE
+            PlainVerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
+            break;
+        CASE_PACKED_YUV_422
+            PlainVerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
+            break;
+        case VLC_CODEC_RGB24:
+            RV24VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
+            break;
+        default:
+            assert( false );
+    }
+}
+
+/*****************************************************************************
+ * PlainVerticalMirror: Mirrors vertically image byte by byte
+ *****************************************************************************
+ * This function mirrors image vertically. It iterates for all lines in
+ * image and for every line, it mirrors byte for byte if needed.
+ * This function works for planar and packed formats
+ *****************************************************************************/
+static void PlainVerticalMirror( picture_t *p_pic, picture_t *p_outpic,
+                                 int i_plane, bool b_left_to_right )
+{
+    uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
+
+    p_in = p_pic->p[i_plane].p_pixels;
+    p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
+        * p_pic->p[i_plane].i_pitch;
+    p_out = p_outpic->p[i_plane].p_pixels;
+
+    while( p_in < p_in_end ) {
+        p_line_start = p_in;
+        p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
+        while( p_in < p_line_end )
+        {
+            /* are we in the left part of the line */
+            if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
+            {
+                if ( b_left_to_right )
+                    *p_out = *p_in;
+                else
+                    *p_out = *( p_line_end - ( p_in - p_line_start ) );
+            }
+            else
+            {
+                if ( b_left_to_right )
+                    *p_out = *( p_line_start + ( p_line_end - p_in ) );
+                else
+                    *p_out = *p_in;
+            }
+            p_in++;
+            p_out++;
+        }
+        p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
+        p_out += p_outpic->p[i_plane].i_pitch
+            - p_outpic->p[i_plane].i_visible_pitch;
+    }
+}
+
+/*****************************************************************************
+ * RV24VerticalMirror: Mirrors vertically image byte by byte for RV24 format
+ *****************************************************************************
+ * This function mirrors image vertically. It iterates for all lines in
+ * image and for every line, it iterates for 3-byte chucks, properly mirroring
+ * them vertically.
+ * This function works only for RV24 formats.
+ *****************************************************************************/
+static void RV24VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
+                                 int i_plane, bool b_left_to_right )
+{
+    uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
+
+    p_in = p_pic->p[i_plane].p_pixels;
+    p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
+        * p_pic->p[i_plane].i_pitch;
+    p_out = p_outpic->p[i_plane].p_pixels;
+
+    while( p_in < p_in_end ) {
+        p_line_start = p_in;
+        p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
+        while( p_in < p_line_end )
+        {
+            /* are we in the left part of the line */
+            if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
+            {
+                if ( b_left_to_right )
+                {
+                    *(p_out++) = *(p_in++);
+                    *(p_out++) = *(p_in++);
+                    *(p_out++) = *(p_in++);
+                }
+                else
+                {
+                    uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
+                    p_in += 3;
+                    *(p_out++) = *p_pixel;
+                    *(p_out++) = *( p_pixel + 1 );
+                    *(p_out++) = * (p_pixel - 1 );
+                }
+            }
+            else
+            {
+                if ( b_left_to_right )
+                {
+                    uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
+                    p_in += 3;
+                    *(p_out++) = *p_pixel;
+                    *(p_out++) = *( p_pixel + 1 );
+                    *(p_out++) = * (p_pixel - 1 );
+                }
+                else
+                {
+                    *(p_out++) = *(p_in++);
+                    *(p_out++) = *(p_in++);
+                    *(p_out++) = *(p_in++);
+                }
+            }
+        }
+        p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
+        p_out += p_outpic->p[i_plane].i_pitch
+            - p_outpic->p[i_plane].i_visible_pitch;
+    }
+}
+
+/*****************************************************************************
+ * HorizontalMirror: Mirrors horizontally image byte by byte
+ *****************************************************************************
+ * This function mirrors image horizontally. It iterates for all lines in
+ * image and for every line, determines if it should be copied, and if it does,
+ * finds opposite line in picture and copies current byte from opposite line.
+ * This function works both for planar, packed and RV24 formats.
+ *****************************************************************************/
+static void HorizontalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
+                             bool b_top_to_bottom )
+{
+    uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
+
+    int i_curr_line = 0;
+    int i_max_lines = p_pic->p[i_plane].i_visible_lines;
+
+    p_in = p_pic->p[i_plane].p_pixels;
+    p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
+        * p_pic->p[i_plane].i_pitch;
+    p_out = p_outpic->p[i_plane].p_pixels;
+
+    while( p_in < p_in_end )
+    {
+        p_line_start = p_in;
+        p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
+        while( p_in < p_line_end )
+        {
+            uint8_t *p_inverse_line;
+            /* are we in the upper part of the picture */
+            if ( i_curr_line < i_max_lines/2 )
+            {
+                if ( b_top_to_bottom )
+                {
+                    *p_out = *p_in;
+                }
+                else
+                {
+                    /* determines line inverse to current line */
+                    p_inverse_line = p_pic->p[i_plane].p_pixels +
+                        ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
+                    *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
+                }
+            }
+            else
+            {
+                if ( b_top_to_bottom )
+                {
+                    /* determines line inverse to current line */
+                    p_inverse_line = p_pic->p[i_plane].p_pixels +
+                        ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
+                    *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
+                }
+                else
+                {
+                    *p_out = *p_in;
+                }
+            }
+            p_in++;
+            p_out++;
+        }
+        i_curr_line++;
+        p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
+        p_out += p_outpic->p[i_plane].i_pitch
+            - p_outpic->p[i_plane].i_visible_pitch;
+    }
+}
+
+static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
+                            vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    (void)oldval;    (void)p_data;
+    filter_t *p_filter = (filter_t*)p_this;
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    if( !strcmp( psz_var, CFG_PREFIX "split" ) )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->i_split = newval.i_int;
+        vlc_mutex_unlock( &p_sys->lock );
+    }
+    else /* CFG_PREFIX "direction" */
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->i_direction = newval.i_int;
+        vlc_mutex_unlock( &p_sys->lock );
+    }
+
+    return VLC_SUCCESS;
+}
-- 
1.6.0.4

