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