6d54149bf3128d10532bd56c9ff6895d389a06df
[vlc.git] / modules / video_filter / mirror.c
1 /*****************************************************************************
2  * mirror.c : Mirror video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <assert.h>
36 #include <vlc_filter.h>
37 #include "filter_picture.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  Create      ( vlc_object_t * );
43 static void Destroy     ( vlc_object_t * );
44
45 static picture_t *Filter( filter_t *, picture_t * );
46 static void VerticalMirror( picture_t *, picture_t *, int plane, bool );
47 static void HorizontalMirror( picture_t *, picture_t *, int, bool );
48 static void PlanarVerticalMirror( picture_t *, picture_t *, int plane, bool );
49 static void YUV422VerticalMirror( picture_t *, picture_t *, int plane, bool, bool );
50 static void RV24VerticalMirror( picture_t *, picture_t *, int plane, bool );
51 static void RV32VerticalMirror( picture_t *, picture_t *, int plane, bool );
52
53 static void YUV422Mirror2Pixels( uint8_t *, uint8_t *, bool );
54
55 static const char *const ppsz_filter_options[] = {
56     "split", "direction", NULL
57 };
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 #define ORIENTATION_TEXT N_("Mirror orientation")
63 #define ORIENTATION_LONGTEXT N_("Defines orientation of the mirror splitting. \
64     Can be vertical or horizontal" )
65 static const int pi_orientation_values[] = { 0, 1 };
66 static const char *const ppsz_orientation_descriptions[] = {
67   N_("Vertical"), N_("Horizontal") };
68
69 #define DIRECTION_TEXT N_("Direction")
70 #define DIRECTION_LONGTEXT N_("Direction of the mirroring" )
71 static const int pi_direction_values[] = { 0, 1 };
72 static const char *const ppsz_direction_descriptions[] = {
73   N_("Left to right/Top to bottom"), N_("Right to left/Bottom to top") };
74
75 #define CFG_PREFIX "mirror-"
76
77 vlc_module_begin ()
78     set_description( N_("Mirror video filter") )
79     set_shortname( N_("Mirror video" ))
80     set_help( N_("Splits video in two same parts, like in the mirror") )
81     set_category( CAT_VIDEO )
82     set_subcategory( SUBCAT_VIDEO_VFILTER )
83     set_capability( "video filter2", 0 )
84     add_integer( CFG_PREFIX "split", 0, NULL, ORIENTATION_TEXT,
85                 ORIENTATION_LONGTEXT, false )
86         change_integer_list( pi_orientation_values,
87                             ppsz_orientation_descriptions, NULL )
88     add_integer( CFG_PREFIX "direction", 0, NULL, DIRECTION_TEXT,
89                 DIRECTION_LONGTEXT, false )
90         change_integer_list( pi_direction_values, ppsz_direction_descriptions,
91                             NULL )
92     set_callbacks( Create, Destroy )
93 vlc_module_end ()
94
95 /*****************************************************************************
96  * callback prototypes
97  *****************************************************************************/
98 static int FilterCallback( vlc_object_t *, char const *,
99                            vlc_value_t, vlc_value_t, void * );
100
101 /*****************************************************************************
102  * filter_sys_t: adjust filter method descriptor
103  *****************************************************************************/
104 struct filter_sys_t
105 {
106     int i_split;
107     int i_direction;
108     vlc_mutex_t lock;
109 };
110
111 /*****************************************************************************
112  * Create: allocates Mirror video thread output method
113  *****************************************************************************
114  * This function allocates and initializes a Mirror vout method.
115  *****************************************************************************/
116 static int Create( vlc_object_t *p_this )
117 {
118     filter_t *p_filter = (filter_t *)p_this;
119     filter_sys_t *p_sys;
120
121     switch( p_filter->fmt_in.video.i_chroma )
122     {
123         CASE_PLANAR_YUV_SQUARE
124             break;
125         CASE_PACKED_YUV_422
126             break;
127         case VLC_CODEC_RGB24:
128         case VLC_CODEC_RGB32:
129             break;
130
131         default:
132             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
133                      (char*)&(p_filter->fmt_in.video.i_chroma) );
134             return VLC_EGENERIC;
135     }
136
137     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
138     {
139         msg_Err( p_filter, "Input and output chromas don't match" );
140         return VLC_EGENERIC;
141     }
142
143     /* Allocate structure */
144     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
145     if( p_filter->p_sys == NULL )
146         return VLC_ENOMEM;
147
148     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
149                        p_filter->p_cfg );
150     p_sys->i_split = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "split" );
151     p_sys->i_direction = var_CreateGetIntegerCommand( p_filter,
152                                                     CFG_PREFIX "direction" );
153
154     vlc_mutex_init( &p_sys->lock );
155
156     var_AddCallback( p_filter, CFG_PREFIX "split", FilterCallback, NULL );
157     var_AddCallback( p_filter, CFG_PREFIX "direction", FilterCallback, NULL );
158
159     p_filter->pf_video_filter = Filter;
160
161     return VLC_SUCCESS;
162 }
163
164 /*****************************************************************************
165  * Destroy: destroy Mirror video thread output method
166  *****************************************************************************
167  * Terminate an output method created by MirrorCreateOutputMethod
168  *****************************************************************************/
169 static void Destroy( vlc_object_t *p_this )
170 {
171     filter_t *p_filter = (filter_t *)p_this;
172
173     var_DelCallback( p_filter, CFG_PREFIX "split", FilterCallback, NULL );
174     var_DelCallback( p_filter, CFG_PREFIX "direction", FilterCallback, NULL );
175
176     vlc_mutex_destroy( &p_filter->p_sys->lock );
177     free( p_filter->p_sys );
178 }
179
180 /*****************************************************************************
181  * Render: displays previously rendered output
182  *****************************************************************************
183  * This function send the currently rendered image to Mirror image, waits
184  * until it is displayed and switch the two rendering buffers, preparing next
185  * frame.
186  *****************************************************************************/
187 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
188 {
189     picture_t *p_outpic;
190     bool b_vertical_split, b_left_to_right;
191     int i_index;
192
193     if( !p_pic ) return NULL;
194
195     filter_sys_t *p_sys = p_filter->p_sys;
196     vlc_mutex_lock( &p_sys->lock );
197     b_vertical_split = p_sys->i_split == 0 ? true : false;
198     b_left_to_right = p_sys->i_direction == 0 ? true : false;
199     vlc_mutex_unlock( &p_sys->lock );
200
201     p_outpic = filter_NewPicture( p_filter );
202     if( !p_outpic )
203     {
204         msg_Warn( p_filter, "can't get output picture" );
205         picture_Release( p_pic );
206         return NULL;
207     }
208
209     for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
210     {
211         if ( b_vertical_split )
212             VerticalMirror( p_pic, p_outpic, i_index, b_left_to_right );
213         else
214             HorizontalMirror( p_pic, p_outpic, i_index, b_left_to_right );
215     }
216
217     return CopyInfoAndRelease( p_outpic, p_pic );
218 }
219
220 /*****************************************************************************
221  * VerticalMirror: Mirrors vertically image
222  *****************************************************************************
223  * This function is a simple delegate to concrete function for vertical
224  * mirroring depending on the input format.
225  *****************************************************************************/
226 static void VerticalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
227                            bool b_left_to_right )
228 {
229     switch( p_pic->format.i_chroma )
230     {
231         CASE_PLANAR_YUV_SQUARE
232             PlanarVerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
233             break;
234         case VLC_CODEC_YUYV:
235         case VLC_CODEC_YVYU:
236             YUV422VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right,
237                                  true );
238             break;
239         case VLC_CODEC_UYVY:
240         case VLC_CODEC_CYUV:
241             YUV422VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right,
242                                  false );
243             break;
244         case VLC_CODEC_RGB24:
245             RV24VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
246             break;
247         case VLC_CODEC_RGB32:
248             RV32VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
249             break;
250         default:
251             assert( false );
252     }
253 }
254
255 /*****************************************************************************
256  * PlanarVerticalMirror: Mirrors vertically image byte by byte
257  *****************************************************************************
258  * This function mirrors image vertically. It iterates for all lines in
259  * image and for every line, it mirrors byte for byte if needed.
260  * This function works for planar formats only.
261  *****************************************************************************/
262 static void PlanarVerticalMirror( picture_t *p_pic, picture_t *p_outpic,
263                                  int i_plane, bool b_left_to_right )
264 {
265     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
266
267     p_in = p_pic->p[i_plane].p_pixels;
268     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
269         * p_pic->p[i_plane].i_pitch;
270     p_out = p_outpic->p[i_plane].p_pixels;
271
272     while( p_in < p_in_end ) {
273         p_line_start = p_in;
274         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
275         while( p_in < p_line_end )
276         {
277             /* are we in the left part of the line */
278             if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
279             {
280                 if ( b_left_to_right )
281                     *p_out = *p_in;
282                 else
283                     *p_out = *( p_line_end - ( p_in - p_line_start ) );
284             }
285             else
286             {
287                 if ( b_left_to_right )
288                     *p_out = *( p_line_start + ( p_line_end - p_in ) );
289                 else
290                     *p_out = *p_in;
291             }
292             p_in++;
293             p_out++;
294         }
295         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
296         p_out += p_outpic->p[i_plane].i_pitch
297             - p_outpic->p[i_plane].i_visible_pitch;
298     }
299 }
300
301 /*****************************************************************************
302  * YUV422VerticalMirror: Mirrors vertically image byte by byte for YUV422 format
303  *****************************************************************************
304  * This function mirrors image vertically. It iterates for all lines in
305  * image and for every line, it iterates for 4-byte chucks, properly mirroring
306  * them vertically (swapping Y components and keeping Cb and Cr components).
307  * This function works only for YUV422 packed formats.
308  *****************************************************************************/
309 static void YUV422VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
310                                  int i_plane, bool b_left_to_right,
311                                  bool b_y_is_odd )
312 {
313     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
314
315     p_in = p_pic->p[i_plane].p_pixels;
316     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
317         * p_pic->p[i_plane].i_pitch;
318     p_out = p_outpic->p[i_plane].p_pixels;
319
320     while( p_in < p_in_end )
321     {
322         p_line_start = p_in;
323         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
324         while( p_in < p_line_end )
325         {
326             /* are we in the left part of the line */
327             if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
328             {
329                 if ( b_left_to_right )
330                 {
331                     *p_out++ = *p_in++;
332                     *p_out++ = *p_in++;
333                     *p_out++ = *p_in++;
334                     *p_out++ = *p_in++;
335                 }
336                 else
337                 {
338                     uint8_t *p_start = p_line_end - ( p_in - p_line_start );
339                     YUV422Mirror2Pixels( p_out, p_start, b_y_is_odd );
340                     p_in += 4;
341                     p_out += 4;
342                 }
343             }
344             else
345             {
346                 if ( b_left_to_right )
347                 {
348                     uint8_t *p_start = p_line_end - ( p_in - p_line_start );
349                     YUV422Mirror2Pixels( p_out, p_start, b_y_is_odd );
350                     p_in += 4;
351                     p_out += 4;
352                 }
353                 else
354                 {
355                     *p_out++ = *p_in++;
356                     *p_out++ = *p_in++;
357                     *p_out++ = *p_in++;
358                     *p_out++ = *p_in++;
359                 }
360             }
361         }
362         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
363         p_out += p_outpic->p[i_plane].i_pitch
364             - p_outpic->p[i_plane].i_visible_pitch;
365     }
366 }
367
368 /*****************************************************************************
369  * YUV422Mirror2Pixels: Mirrors 2 consecutive pixels
370  *****************************************************************************
371  * This function mirrors two consecutive pixels of 4 byte size. Depending of
372  * position of Y components (b_y_is_odd is true if Y components are first and
373  * third (like in YUYV or YVYU), they are properly swapped, and Cb and Cr
374  * component positions are preserved.
375  *****************************************************************************/
376 static void YUV422Mirror2Pixels( uint8_t* p_dst, uint8_t *p_src,
377                                 bool b_y_is_odd )
378 {
379     if ( b_y_is_odd )
380     {
381         /* swap Y components */
382         *p_dst = *( p_src + 2 );
383         *( p_dst + 2 ) = *p_src;
384         /* copy Cb and Cr components */
385         *( p_dst + 1 ) = *( p_src + 1 );
386         *( p_dst + 3 ) = *( p_src + 3 );
387     }
388     else{
389         /* swap Y components */
390         *( p_dst + 1 )= *( p_src + 3 );
391         *( p_dst + 3 ) = *( p_src + 1);
392         /* copy Cb and Cr components */
393         *p_dst = *( p_src + 2 );
394         *( p_dst + 2 ) = *p_src;
395     }
396 }
397
398 /*****************************************************************************
399  * RV24VerticalMirror: Mirrors vertically image byte by byte for RV24 format
400  *****************************************************************************
401  * This function mirrors image vertically. It iterates for all lines in
402  * image and for every line, it iterates for 3-byte chunks.
403  * This function works only for RV24 formats.
404  *****************************************************************************/
405 static void RV24VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
406                                  int i_plane, bool b_left_to_right )
407 {
408     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
409
410     p_in = p_pic->p[i_plane].p_pixels;
411     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
412         * p_pic->p[i_plane].i_pitch;
413     p_out = p_outpic->p[i_plane].p_pixels;
414
415     while( p_in < p_in_end )
416     {
417         p_line_start = p_in;
418         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
419         while( p_in < p_line_end )
420         {
421             /* are we in the left part of the line */
422             if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
423             {
424                 if ( b_left_to_right )
425                 {
426                     *p_out++ = *p_in++;
427                     *p_out++ = *p_in++;
428                     *p_out++ = *p_in++;
429                 }
430                 else
431                 {
432                     uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
433                     p_in += 3;
434                     *p_out++ = *p_pixel++;
435                     *p_out++ = *p_pixel++;
436                     *p_out++ = *p_pixel++;
437                 }
438             }
439             else
440             {
441                 if ( b_left_to_right )
442                 {
443                     uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
444                     p_in += 3;
445                     *p_out++ = *p_pixel++;
446                     *p_out++ = *p_pixel++;
447                     *p_out++ = *p_pixel++;
448                 }
449                 else
450                 {
451                     *p_out++ = *p_in++;
452                     *p_out++ = *p_in++;
453                     *p_out++ = *p_in++;
454                 }
455             }
456         }
457         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
458         p_out += p_outpic->p[i_plane].i_pitch
459             - p_outpic->p[i_plane].i_visible_pitch;
460     }
461 }
462
463 /*****************************************************************************
464  * RV32VerticalMirror: Mirrors vertically image byte by byte for RV32 format
465  *****************************************************************************
466  * This function mirrors image vertically. It iterates for all lines in
467  * image and for every line, it iterates for 4-byte chunks as 32-bit pointers.
468  * This function works only for RV32 formats.
469  *****************************************************************************/
470 static void RV32VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
471                                  int i_plane, bool b_left_to_right )
472 {
473     uint8_t *p_in, *p_in_end, *p_out;
474
475     p_in = p_pic->p[i_plane].p_pixels;
476     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
477         * p_pic->p[i_plane].i_pitch;
478     p_out = p_outpic->p[i_plane].p_pixels;
479
480     while( p_in < p_in_end )
481     {
482         uint32_t *p_in32, *p_out32, *p_line_start32, *p_line_end32;
483         p_in32 = (uint32_t*) p_in;
484         p_out32 = (uint32_t*) p_out;
485         p_line_start32 = p_in32;
486         p_line_end32 = (uint32_t*) ( p_in + p_pic->p[i_plane].i_visible_pitch) ;
487
488         while( p_in32 < p_line_end32 )
489         {
490             /* are we in the left part of the line */
491             if ( p_in32 < p_line_start32 + ( p_line_end32 - p_line_start32 ) / 2 )
492             {
493                 if ( b_left_to_right )
494                 {
495                     *p_out32++ = *p_in32++;
496                 }
497                 else
498                 {
499                     uint32_t *p_pixel32 = p_line_end32 - ( p_in32 - p_line_start32 );
500                     p_in32++;
501                     *p_out++ = *p_pixel32;
502                 }
503             }
504             else
505             {
506                 if ( b_left_to_right )
507                 {
508                     uint32_t *p_pixel32 = p_line_end32 - ( p_in32 - p_line_start32 );
509                     p_in32++;
510                     *p_out++ = *p_pixel32;
511                 }
512                 else
513                 {
514                     *p_out32++ = *p_in32++;
515                 }
516             }
517         }
518         p_in = (uint8_t*) p_in32;
519         p_out = (uint8_t*) p_out32;
520         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
521         p_out += p_outpic->p[i_plane].i_pitch
522             - p_outpic->p[i_plane].i_visible_pitch;
523     }
524 }
525
526 /*****************************************************************************
527  * HorizontalMirror: Mirrors horizontally image byte by byte
528  *****************************************************************************
529  * This function mirrors image horizontally. It iterates for all lines in
530  * image and for every line, determines if it should be copied, and if it does,
531  * finds opposite line in picture and copies current byte from opposite line.
532  * This function works both for planar, packed and RV24 formats.
533  *****************************************************************************/
534 static void HorizontalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
535                              bool b_top_to_bottom )
536 {
537     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
538
539     int i_curr_line = 0;
540     int i_max_lines = p_pic->p[i_plane].i_visible_lines;
541
542     p_in = p_pic->p[i_plane].p_pixels;
543     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
544         * p_pic->p[i_plane].i_pitch;
545     p_out = p_outpic->p[i_plane].p_pixels;
546
547     while( p_in < p_in_end )
548     {
549         p_line_start = p_in;
550         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
551         while( p_in < p_line_end )
552         {
553             uint8_t *p_inverse_line;
554             /* are we in the upper part of the picture */
555             if ( i_curr_line < i_max_lines/2 )
556             {
557                 if ( b_top_to_bottom )
558                 {
559                     *p_out = *p_in;
560                 }
561                 else
562                 {
563                     /* determines line inverse to current line */
564                     p_inverse_line = p_pic->p[i_plane].p_pixels +
565                         ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
566                     *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
567                 }
568             }
569             else
570             {
571                 if ( b_top_to_bottom )
572                 {
573                     /* determines line inverse to current line */
574                     p_inverse_line = p_pic->p[i_plane].p_pixels +
575                         ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
576                     *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
577                 }
578                 else
579                 {
580                     *p_out = *p_in;
581                 }
582             }
583             p_in++;
584             p_out++;
585         }
586         i_curr_line++;
587         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
588         p_out += p_outpic->p[i_plane].i_pitch
589             - p_outpic->p[i_plane].i_visible_pitch;
590     }
591 }
592
593 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
594                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
595 {
596     (void)oldval;    (void)p_data;
597     filter_t *p_filter = (filter_t*)p_this;
598     filter_sys_t *p_sys = p_filter->p_sys;
599
600     if( !strcmp( psz_var, CFG_PREFIX "split" ) )
601     {
602         vlc_mutex_lock( &p_sys->lock );
603         p_sys->i_split = newval.i_int;
604         vlc_mutex_unlock( &p_sys->lock );
605     }
606     else /* CFG_PREFIX "direction" */
607     {
608         vlc_mutex_lock( &p_sys->lock );
609         p_sys->i_direction = newval.i_int;
610         vlc_mutex_unlock( &p_sys->lock );
611     }
612
613     return VLC_SUCCESS;
614 }