]> git.sesse.net Git - vlc/blob - modules/video_filter/transform.c
40eab9db004f89a6dfb51a95acbd4f3e87abe5b6
[vlc] / modules / video_filter / transform.c
1 /*****************************************************************************
2  * transform.c : transform image module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
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 #include <vlc_vout.h>
35
36 #include "filter_common.h"
37 #include "filter_picture.h"
38
39 #define TRANSFORM_MODE_HFLIP   1
40 #define TRANSFORM_MODE_VFLIP   2
41 #define TRANSFORM_MODE_90      3
42 #define TRANSFORM_MODE_180     4
43 #define TRANSFORM_MODE_270     5
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 static int  Create    ( vlc_object_t * );
49 static void Destroy   ( vlc_object_t * );
50
51 static int  Init      ( vout_thread_t * );
52 static void End       ( vout_thread_t * );
53 static void Render    ( vout_thread_t *, picture_t * );
54
55 static void FilterPlanar( vout_thread_t *, const picture_t *, picture_t * );
56 static void FilterI422( vout_thread_t *, const picture_t *, picture_t * );
57 static void FilterYUYV( vout_thread_t *, const picture_t *, picture_t * );
58
59 static int  SendEvents( vlc_object_t *, char const *,
60                         vlc_value_t, vlc_value_t, void * );
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 #define TYPE_TEXT N_("Transform type")
66 #define TYPE_LONGTEXT N_("One of '90', '180', '270', 'hflip' and 'vflip'")
67
68 static const char *const type_list[] = { "90", "180", "270", "hflip", "vflip" };
69 static const char *const type_list_text[] = { N_("Rotate by 90 degrees"),
70   N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
71   N_("Flip horizontally"), N_("Flip vertically") };
72
73 #define CFG_PREFIX "transform-"
74
75 vlc_module_begin();
76     set_description( N_("Video transformation filter") );
77     set_shortname( N_("Transformation"));
78     set_capability( "video filter", 0 );
79     set_category( CAT_VIDEO );
80     set_subcategory( SUBCAT_VIDEO_VFILTER );
81
82     add_string( CFG_PREFIX "type", "90", NULL,
83                           TYPE_TEXT, TYPE_LONGTEXT, false);
84         change_string_list( type_list, type_list_text, 0);
85
86     add_shortcut( "transform" );
87     set_callbacks( Create, Destroy );
88 vlc_module_end();
89
90 static const char *const ppsz_filter_options[] = {
91     "type", NULL
92 };
93
94 /*****************************************************************************
95  * vout_sys_t: Transform video output method descriptor
96  *****************************************************************************
97  * This structure is part of the video output thread descriptor.
98  * It describes the Transform specific properties of an output thread.
99  *****************************************************************************/
100 struct vout_sys_t
101 {
102     int i_mode;
103     bool b_rotation;
104     vout_thread_t *p_vout;
105
106     void (*pf_filter)( vout_thread_t *, const picture_t *, picture_t * );
107 };
108
109 /*****************************************************************************
110  * Control: control facility for the vout (forwards to child vout)
111  *****************************************************************************/
112 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
113 {
114     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
115 }
116
117 /*****************************************************************************
118  * Create: allocates Transform video thread output method
119  *****************************************************************************
120  * This function allocates and initializes a Transform vout method.
121  *****************************************************************************/
122 static int Create( vlc_object_t *p_this )
123 {
124     vout_thread_t *p_vout = (vout_thread_t *)p_this;
125     char *psz_method;
126
127     /* Allocate structure */
128     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
129     if( p_vout->p_sys == NULL )
130     {
131         msg_Err( p_vout, "out of memory" );
132         return VLC_ENOMEM;
133     }
134
135     p_vout->pf_init = Init;
136     p_vout->pf_end = End;
137     p_vout->pf_manage = NULL;
138     p_vout->pf_render = Render;
139     p_vout->pf_display = NULL;
140     p_vout->pf_control = Control;
141
142     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
143                            p_vout->p_cfg );
144
145     /* Look what method was requested */
146     psz_method = var_CreateGetNonEmptyString( p_vout, "transform-type" );
147
148     switch( p_vout->fmt_in.i_chroma )
149     {
150         CASE_PLANAR_YUV_SQUARE
151         case VLC_FOURCC('G','R','E','Y'):
152             p_vout->p_sys->pf_filter = FilterPlanar;
153             break;
154
155         case VLC_FOURCC('I','4','2','2'):
156         case VLC_FOURCC('J','4','2','2'):
157             p_vout->p_sys->pf_filter = FilterI422;
158             break;
159
160         CASE_PACKED_YUV_422
161             p_vout->p_sys->pf_filter = FilterYUYV;
162             break;
163
164         default:
165             msg_Err( p_vout, "Unsupported chroma" );
166             free( p_vout->p_sys );
167             return VLC_EGENERIC;
168     }
169
170     if( psz_method == NULL )
171     {
172         msg_Err( p_vout, "configuration variable %s empty", "transform-type" );
173         msg_Err( p_vout, "no valid transform mode provided, using '90'" );
174         p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
175         p_vout->p_sys->b_rotation = 1;
176     }
177     else
178     {
179         if( !strcmp( psz_method, "hflip" ) )
180         {
181             p_vout->p_sys->i_mode = TRANSFORM_MODE_HFLIP;
182             p_vout->p_sys->b_rotation = 0;
183         }
184         else if( !strcmp( psz_method, "vflip" ) )
185         {
186             p_vout->p_sys->i_mode = TRANSFORM_MODE_VFLIP;
187             p_vout->p_sys->b_rotation = 0;
188         }
189         else if( !strcmp( psz_method, "90" ) )
190         {
191             p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
192             p_vout->p_sys->b_rotation = 1;
193         }
194         else if( !strcmp( psz_method, "180" ) )
195         {
196             p_vout->p_sys->i_mode = TRANSFORM_MODE_180;
197             p_vout->p_sys->b_rotation = 0;
198         }
199         else if( !strcmp( psz_method, "270" ) )
200         {
201             p_vout->p_sys->i_mode = TRANSFORM_MODE_270;
202             p_vout->p_sys->b_rotation = 1;
203         }
204         else
205         {
206             msg_Err( p_vout, "no valid transform mode provided, using '90'" );
207             p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
208             p_vout->p_sys->b_rotation = 1;
209         }
210
211         free( psz_method );
212     }
213
214     return VLC_SUCCESS;
215 }
216
217 /*****************************************************************************
218  * Init: initialize Transform video thread output method
219  *****************************************************************************/
220 static int Init( vout_thread_t *p_vout )
221 {
222     int i_index;
223     picture_t *p_pic;
224     video_format_t fmt;
225
226     I_OUTPUTPICTURES = 0;
227     memset( &fmt, 0, sizeof(video_format_t) );
228
229     /* Initialize the output structure */
230     p_vout->output.i_chroma = p_vout->render.i_chroma;
231     p_vout->output.i_width  = p_vout->render.i_width;
232     p_vout->output.i_height = p_vout->render.i_height;
233     p_vout->output.i_aspect = p_vout->render.i_aspect;
234     p_vout->fmt_out = p_vout->fmt_in;
235     fmt = p_vout->fmt_out;
236
237     /* Try to open the real video output */
238     msg_Dbg( p_vout, "spawning the real video output" );
239
240     if( p_vout->p_sys->b_rotation )
241     {
242         fmt.i_width = p_vout->fmt_out.i_height;
243         fmt.i_visible_width = p_vout->fmt_out.i_visible_height;
244         fmt.i_x_offset = p_vout->fmt_out.i_y_offset;
245
246         fmt.i_height = p_vout->fmt_out.i_width;
247         fmt.i_visible_height = p_vout->fmt_out.i_visible_width;
248         fmt.i_y_offset = p_vout->fmt_out.i_x_offset;
249
250         fmt.i_aspect = VOUT_ASPECT_FACTOR *
251             (uint64_t)VOUT_ASPECT_FACTOR / fmt.i_aspect;
252
253         fmt.i_sar_num = p_vout->fmt_out.i_sar_den;
254         fmt.i_sar_den = p_vout->fmt_out.i_sar_num;
255     }
256
257     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
258
259     /* Everything failed */
260     if( p_vout->p_sys->p_vout == NULL )
261     {
262         msg_Err( p_vout, "cannot open vout, aborting" );
263         return VLC_EGENERIC;
264     }
265
266     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
267
268     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
269
270     ADD_PARENT_CALLBACKS( SendEventsToChild );
271
272     return VLC_SUCCESS;
273 }
274
275 /*****************************************************************************
276  * End: terminate Transform video thread output method
277  *****************************************************************************/
278 static void End( vout_thread_t *p_vout )
279 {
280     int i_index;
281
282     /* Free the fake output buffers we allocated */
283     for( i_index = I_OUTPUTPICTURES ; i_index ; )
284     {
285         i_index--;
286         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
287     }
288 }
289
290 /*****************************************************************************
291  * Destroy: destroy Transform video thread output method
292  *****************************************************************************
293  * Terminate an output method created by TransformCreateOutputMethod
294  *****************************************************************************/
295 static void Destroy( vlc_object_t *p_this )
296 {
297     vout_thread_t *p_vout = (vout_thread_t *)p_this;
298
299     if( p_vout->p_sys->p_vout )
300     {
301         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
302         vlc_object_detach( p_vout->p_sys->p_vout );
303         vlc_object_release( p_vout->p_sys->p_vout );
304     }
305
306     DEL_PARENT_CALLBACKS( SendEventsToChild );
307
308     free( p_vout->p_sys );
309 }
310
311 /*****************************************************************************
312  * Render: displays previously rendered output
313  *****************************************************************************
314  * This function send the currently rendered image to Transform image, waits
315  * until it is displayed and switch the two rendering buffers, preparing next
316  * frame.
317  *****************************************************************************/
318 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
319 {
320     picture_t *p_outpic;
321
322     /* This is a new frame. Get a structure from the video_output. */
323     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
324               == NULL )
325     {
326         if( p_vout->b_die || p_vout->b_error )
327         {
328             return;
329         }
330         msleep( VOUT_OUTMEM_SLEEP );
331     }
332
333     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
334     vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
335
336     p_vout->p_sys->pf_filter( p_vout, p_pic, p_outpic );
337
338     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
339
340     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
341 }
342
343 /*****************************************************************************
344  * SendEvents: forward mouse and keyboard events to the parent p_vout
345  *****************************************************************************/
346 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
347                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
348 {
349     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
350     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
351     vlc_value_t sentval = newval;
352
353     /* Translate the mouse coordinates */
354     if( !strcmp( psz_var, "mouse-x" ) )
355     {
356         switch( p_vout->p_sys->i_mode )
357         {
358         case TRANSFORM_MODE_270:
359             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
360                              - sentval.i_int;
361         case TRANSFORM_MODE_90:
362             var_Set( p_vout, "mouse-y", sentval );
363             return VLC_SUCCESS;
364
365         case TRANSFORM_MODE_180:
366         case TRANSFORM_MODE_HFLIP:
367             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
368                              - sentval.i_int;
369             break;
370
371         case TRANSFORM_MODE_VFLIP:
372         default:
373             break;
374         }
375     }
376     else if( !strcmp( psz_var, "mouse-y" ) )
377     {
378         switch( p_vout->p_sys->i_mode )
379         {
380         case TRANSFORM_MODE_90:
381             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
382                              - sentval.i_int;
383         case TRANSFORM_MODE_270:
384             var_Set( p_vout, "mouse-x", sentval );
385             return VLC_SUCCESS;
386
387         case TRANSFORM_MODE_180:
388         case TRANSFORM_MODE_VFLIP:
389             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
390                              - sentval.i_int;
391             break;
392
393         case TRANSFORM_MODE_HFLIP:
394         default:
395             break;
396         }
397     }
398
399     var_Set( p_vout, psz_var, sentval );
400
401     return VLC_SUCCESS;
402 }
403
404 /*****************************************************************************
405  * SendEventsToChild: forward events to the child/children vout
406  *****************************************************************************/
407 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
408                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
409 {
410     VLC_UNUSED(p_data); VLC_UNUSED(oldval);
411     vout_thread_t *p_vout = (vout_thread_t *)p_this;
412     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
413     return VLC_SUCCESS;
414 }
415
416 static void FilterPlanar( vout_thread_t *p_vout,
417                           const picture_t *p_pic, picture_t *p_outpic )
418 {
419     int i_index;
420     switch( p_vout->p_sys->i_mode )
421     {
422         case TRANSFORM_MODE_90:
423             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
424             {
425                 int i_pitch = p_pic->p[i_index].i_pitch;
426
427                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
428
429                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
430                 uint8_t *p_out_end = p_out +
431                     p_outpic->p[i_index].i_visible_lines *
432                     p_outpic->p[i_index].i_pitch;
433
434                 for( ; p_out < p_out_end ; )
435                 {
436                     uint8_t *p_line_end;
437
438                     p_out_end -= p_outpic->p[i_index].i_pitch
439                                   - p_outpic->p[i_index].i_visible_pitch;
440                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
441                         i_pitch;
442
443                     for( ; p_in < p_line_end ; )
444                     {
445                         p_line_end -= i_pitch;
446                         *(--p_out_end) = *p_line_end;
447                     }
448
449                     p_in++;
450                 }
451             }
452             break;
453
454         case TRANSFORM_MODE_180:
455             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
456             {
457                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
458                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
459                                             * p_pic->p[i_index].i_pitch;
460
461                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
462
463                 for( ; p_in < p_in_end ; )
464                 {
465                     uint8_t *p_line_start = p_in_end
466                                              - p_pic->p[i_index].i_pitch;
467                     p_in_end -= p_pic->p[i_index].i_pitch
468                                  - p_pic->p[i_index].i_visible_pitch;
469
470                     for( ; p_line_start < p_in_end ; )
471                     {
472                         *p_out++ = *(--p_in_end);
473                     }
474
475                     p_out += p_outpic->p[i_index].i_pitch
476                               - p_outpic->p[i_index].i_visible_pitch;
477                 }
478             }
479             break;
480
481         case TRANSFORM_MODE_270:
482             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
483             {
484                 int i_pitch = p_pic->p[i_index].i_pitch;
485
486                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
487
488                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
489                 uint8_t *p_out_end = p_out +
490                     p_outpic->p[i_index].i_visible_lines *
491                     p_outpic->p[i_index].i_pitch;
492
493                 for( ; p_out < p_out_end ; )
494                 {
495                     uint8_t *p_in_end;
496
497                     p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
498                         i_pitch;
499
500                     for( ; p_in < p_in_end ; )
501                     {
502                         p_in_end -= i_pitch;
503                         *p_out++ = *p_in_end;
504                     }
505
506                     p_out += p_outpic->p[i_index].i_pitch
507                               - p_outpic->p[i_index].i_visible_pitch;
508                     p_in++;
509                 }
510             }
511             break;
512
513         case TRANSFORM_MODE_HFLIP:
514             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
515             {
516                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
517                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
518                                             * p_pic->p[i_index].i_pitch;
519
520                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
521
522                 for( ; p_in < p_in_end ; )
523                 {
524                     p_in_end -= p_pic->p[i_index].i_pitch;
525                     vlc_memcpy( p_out, p_in_end,
526                                 p_pic->p[i_index].i_visible_pitch );
527                     p_out += p_pic->p[i_index].i_pitch;
528                 }
529             }
530             break;
531
532         case TRANSFORM_MODE_VFLIP:
533             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
534             {
535                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
536                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
537                                          * p_pic->p[i_index].i_pitch;
538
539                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
540
541                 for( ; p_in < p_in_end ; )
542                 {
543                     uint8_t *p_line_end = p_in
544                                         + p_pic->p[i_index].i_visible_pitch;
545
546                     for( ; p_in < p_line_end ; )
547                     {
548                         *p_out++ = *(--p_line_end);
549                     }
550
551                     p_in += p_pic->p[i_index].i_pitch;
552                 }
553             }
554             break;
555
556         default:
557             break;
558     }
559 }
560
561 static void FilterI422( vout_thread_t *p_vout,
562                         const picture_t *p_pic, picture_t *p_outpic )
563 {
564     int i_index;
565     switch( p_vout->p_sys->i_mode )
566     {
567         case TRANSFORM_MODE_180:
568         case TRANSFORM_MODE_HFLIP:
569         case TRANSFORM_MODE_VFLIP:
570             /* Fall back on the default implementation */
571             FilterPlanar( p_vout, p_pic, p_outpic );
572             return;
573
574         case TRANSFORM_MODE_90:
575             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
576             {
577                 int i_pitch = p_pic->p[i_index].i_pitch;
578
579                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
580
581                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
582                 uint8_t *p_out_end = p_out +
583                     p_outpic->p[i_index].i_visible_lines *
584                     p_outpic->p[i_index].i_pitch;
585
586                 if( i_index == 0 )
587                 {
588                     for( ; p_out < p_out_end ; )
589                     {
590                         uint8_t *p_line_end;
591
592                         p_out_end -= p_outpic->p[i_index].i_pitch
593                                       - p_outpic->p[i_index].i_visible_pitch;
594                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
595                             i_pitch;
596
597                         for( ; p_in < p_line_end ; )
598                         {
599                             p_line_end -= i_pitch;
600                             *(--p_out_end) = *p_line_end;
601                         }
602
603                         p_in++;
604                     }
605                 }
606                 else /* i_index == 1 or 2 */
607                 {
608                     for( ; p_out < p_out_end ; )
609                     {
610                         uint8_t *p_line_end, *p_out_end2;
611
612                         p_out_end -= p_outpic->p[i_index].i_pitch
613                                       - p_outpic->p[i_index].i_visible_pitch;
614                         p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
615                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
616                             i_pitch;
617
618                         for( ; p_in < p_line_end ; )
619                         {
620                             uint8_t p1, p2;
621
622                             p_line_end -= i_pitch;
623                             p1 = *p_line_end;
624                             p_line_end -= i_pitch;
625                             p2 = *p_line_end;
626
627                             /* Trick for (x+y)/2 without overflow, based on
628                              *   x + y == (x ^ y) + 2 * (x & y) */
629                             *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
630                             *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
631                         }
632
633                         p_out_end = p_out_end2;
634                         p_in++;
635                     }
636                 }
637             }
638             break;
639
640         case TRANSFORM_MODE_270:
641             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
642             {
643                 int i_pitch = p_pic->p[i_index].i_pitch;
644
645                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
646
647                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
648                 uint8_t *p_out_end = p_out +
649                     p_outpic->p[i_index].i_visible_lines *
650                     p_outpic->p[i_index].i_pitch;
651
652                 if( i_index == 0 )
653                 {
654                     for( ; p_out < p_out_end ; )
655                     {
656                         uint8_t *p_in_end;
657
658                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
659                             i_pitch;
660
661                         for( ; p_in < p_in_end ; )
662                         {
663                             p_in_end -= i_pitch;
664                             *p_out++ = *p_in_end;
665                         }
666
667                         p_out += p_outpic->p[i_index].i_pitch
668                                   - p_outpic->p[i_index].i_visible_pitch;
669                         p_in++;
670                     }
671                 }
672                 else /* i_index == 1 or 2 */
673                 {
674                     for( ; p_out < p_out_end ; )
675                     {
676                         uint8_t *p_in_end, *p_out2;
677
678                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
679                             i_pitch;
680                         p_out2 = p_out + p_outpic->p[i_index].i_pitch;
681
682                         for( ; p_in < p_in_end ; )
683                         {
684                             uint8_t p1, p2;
685
686                             p_in_end -= i_pitch;
687                             p1 = *p_in_end;
688                             p_in_end -= i_pitch;
689                             p2 = *p_in_end;
690
691                             /* Trick for (x+y)/2 without overflow, based on
692                              *   x + y == (x ^ y) + 2 * (x & y) */
693                             *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
694                             *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
695                         }
696
697                         p_out2 += p_outpic->p[i_index].i_pitch
698                                    - p_outpic->p[i_index].i_visible_pitch;
699                         p_out = p_out2;
700                         p_in++;
701                     }
702                 }
703             }
704             break;
705
706         default:
707             break;
708     }
709 }
710
711 static void FilterYUYV( vout_thread_t *p_vout,
712                         const picture_t *p_pic, picture_t *p_outpic )
713 {
714     int i_index;
715     int i_y_offset, i_u_offset, i_v_offset;
716     if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
717                              &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
718         return;
719
720     switch( p_vout->p_sys->i_mode )
721     {
722         case TRANSFORM_MODE_HFLIP:
723             /* Fall back on the default implementation */
724             FilterPlanar( p_vout, p_pic, p_outpic );
725             return;
726
727         case TRANSFORM_MODE_90:
728             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
729             {
730                 int i_pitch = p_pic->p[i_index].i_pitch;
731
732                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
733
734                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
735                 uint8_t *p_out_end = p_out +
736                     p_outpic->p[i_index].i_visible_lines *
737                     p_outpic->p[i_index].i_pitch;
738
739                 int i_offset  = i_u_offset;
740                 int i_offset2 = i_v_offset;
741                 for( ; p_out < p_out_end ; )
742                 {
743                     uint8_t *p_line_end;
744
745                     p_out_end -= p_outpic->p[i_index].i_pitch
746                                   - p_outpic->p[i_index].i_visible_pitch;
747                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
748                         i_pitch;
749
750                     for( ; p_in < p_line_end ; )
751                     {
752                         p_line_end -= i_pitch;
753                         p_out_end -= 4;
754                         p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
755                         p_out_end[i_u_offset] = p_line_end[i_offset];
756                         p_line_end -= i_pitch;
757                         p_out_end[i_y_offset] = p_line_end[i_y_offset];
758                         p_out_end[i_v_offset] = p_line_end[i_offset2];
759                     }
760
761                     p_in += 2;
762
763                     {
764                         int a = i_offset;
765                         i_offset = i_offset2;
766                         i_offset2 = a;
767                     }
768                 }
769             }
770             break;
771
772         case TRANSFORM_MODE_180:
773             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
774             {
775                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
776                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
777                                             * p_pic->p[i_index].i_pitch;
778
779                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
780
781                 for( ; p_in < p_in_end ; )
782                 {
783                     uint8_t *p_line_start = p_in_end
784                                              - p_pic->p[i_index].i_pitch;
785                     p_in_end -= p_pic->p[i_index].i_pitch
786                                  - p_pic->p[i_index].i_visible_pitch;
787
788                     for( ; p_line_start < p_in_end ; )
789                     {
790                         p_in_end -= 4;
791                         p_out[i_y_offset] = p_in_end[i_y_offset+2];
792                         p_out[i_u_offset] = p_in_end[i_u_offset];
793                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
794                         p_out[i_v_offset] = p_in_end[i_v_offset];
795                         p_out += 4;
796                     }
797
798                     p_out += p_outpic->p[i_index].i_pitch
799                               - p_outpic->p[i_index].i_visible_pitch;
800                 }
801             }
802             break;
803
804         case TRANSFORM_MODE_270:
805             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
806             {
807                 int i_pitch = p_pic->p[i_index].i_pitch;
808
809                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
810
811                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
812                 uint8_t *p_out_end = p_out +
813                     p_outpic->p[i_index].i_visible_lines *
814                     p_outpic->p[i_index].i_pitch;
815
816                 int i_offset  = i_u_offset;
817                 int i_offset2 = i_v_offset;
818                 for( ; p_out < p_out_end ; )
819                 {
820                     uint8_t *p_in_end;
821
822                     p_in_end = p_in
823                              + p_pic->p[i_index].i_visible_lines * i_pitch;
824
825                     for( ; p_in < p_in_end ; )
826                     {
827                         p_in_end -= i_pitch;
828                         p_out[i_y_offset] = p_in_end[i_y_offset];
829                         p_out[i_u_offset] = p_in_end[i_offset];
830                         p_in_end -= i_pitch;
831                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
832                         p_out[i_v_offset] = p_in_end[i_offset2];
833                         p_out += 4;
834                     }
835
836                     p_out += p_outpic->p[i_index].i_pitch
837                            - p_outpic->p[i_index].i_visible_pitch;
838                     p_in += 2;
839
840                     {
841                         int a = i_offset;
842                         i_offset = i_offset2;
843                         i_offset2 = a;
844                     }
845                 }
846             }
847             break;
848
849         case TRANSFORM_MODE_VFLIP:
850             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
851             {
852                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
853                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
854                                          * p_pic->p[i_index].i_pitch;
855
856                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
857
858                 for( ; p_in < p_in_end ; )
859                 {
860                     uint8_t *p_line_end = p_in
861                                         + p_pic->p[i_index].i_visible_pitch;
862
863                     for( ; p_in < p_line_end ; )
864                     {
865                         p_line_end -= 4;
866                         p_out[i_y_offset] = p_line_end[i_y_offset+2];
867                         p_out[i_u_offset] = p_line_end[i_u_offset];
868                         p_out[i_y_offset+2] = p_line_end[i_y_offset];
869                         p_out[i_v_offset] = p_line_end[i_v_offset];
870                         p_out += 4;
871                     }
872
873                     p_in += p_pic->p[i_index].i_pitch;
874                 }
875             }
876             break;
877
878         default:
879             break;
880     }
881 }