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