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