]> git.sesse.net Git - vlc/blob - modules/video_filter/transform.c
vout: Rename vout_Destroy to vout_CloseAndDestroy.
[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     DEL_PARENT_CALLBACKS( SendEventsToChild );
280
281     DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
282
283     /* Free the fake output buffers we allocated */
284     for( i_index = I_OUTPUTPICTURES ; i_index ; )
285     {
286         i_index--;
287         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
288     }
289
290     vout_CloseAndRelease( p_vout->p_sys->p_vout );
291 }
292
293 /*****************************************************************************
294  * Destroy: destroy Transform video thread output method
295  *****************************************************************************
296  * Terminate an output method created by TransformCreateOutputMethod
297  *****************************************************************************/
298 static void Destroy( vlc_object_t *p_this )
299 {
300     vout_thread_t *p_vout = (vout_thread_t *)p_this;
301
302     free( p_vout->p_sys );
303 }
304
305 /*****************************************************************************
306  * Render: displays previously rendered output
307  *****************************************************************************
308  * This function send the currently rendered image to Transform image, waits
309  * until it is displayed and switch the two rendering buffers, preparing next
310  * frame.
311  *****************************************************************************/
312 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
313 {
314     picture_t *p_outpic;
315
316     /* This is a new frame. Get a structure from the video_output. */
317     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
318               == NULL )
319     {
320         if( !vlc_object_alive (p_vout) || p_vout->b_error )
321         {
322             return;
323         }
324         msleep( VOUT_OUTMEM_SLEEP );
325     }
326
327     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
328     vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
329
330     p_vout->p_sys->pf_filter( p_vout, p_pic, p_outpic );
331
332     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
333
334     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
335 }
336
337 /*****************************************************************************
338  * SendEvents: forward mouse and keyboard events to the parent p_vout
339  *****************************************************************************/
340 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
341                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
342 {
343     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
344     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
345     vlc_value_t sentval = newval;
346
347     /* Translate the mouse coordinates */
348     if( !strcmp( psz_var, "mouse-x" ) )
349     {
350         switch( p_vout->p_sys->i_mode )
351         {
352         case TRANSFORM_MODE_270:
353             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
354                              - sentval.i_int;
355         case TRANSFORM_MODE_90:
356             var_Set( p_vout, "mouse-y", sentval );
357             return VLC_SUCCESS;
358
359         case TRANSFORM_MODE_180:
360         case TRANSFORM_MODE_HFLIP:
361             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
362                              - sentval.i_int;
363             break;
364
365         case TRANSFORM_MODE_VFLIP:
366         default:
367             break;
368         }
369     }
370     else if( !strcmp( psz_var, "mouse-y" ) )
371     {
372         switch( p_vout->p_sys->i_mode )
373         {
374         case TRANSFORM_MODE_90:
375             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
376                              - sentval.i_int;
377         case TRANSFORM_MODE_270:
378             var_Set( p_vout, "mouse-x", sentval );
379             return VLC_SUCCESS;
380
381         case TRANSFORM_MODE_180:
382         case TRANSFORM_MODE_VFLIP:
383             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
384                              - sentval.i_int;
385             break;
386
387         case TRANSFORM_MODE_HFLIP:
388         default:
389             break;
390         }
391     }
392
393     var_Set( p_vout, psz_var, sentval );
394
395     return VLC_SUCCESS;
396 }
397
398 /*****************************************************************************
399  * SendEventsToChild: forward events to the child/children vout
400  *****************************************************************************/
401 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
402                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
403 {
404     VLC_UNUSED(p_data); VLC_UNUSED(oldval);
405     vout_thread_t *p_vout = (vout_thread_t *)p_this;
406     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
407     return VLC_SUCCESS;
408 }
409
410 static void FilterPlanar( vout_thread_t *p_vout,
411                           const picture_t *p_pic, picture_t *p_outpic )
412 {
413     int i_index;
414     switch( p_vout->p_sys->i_mode )
415     {
416         case TRANSFORM_MODE_90:
417             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
418             {
419                 int i_pitch = p_pic->p[i_index].i_pitch;
420
421                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
422
423                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
424                 uint8_t *p_out_end = p_out +
425                     p_outpic->p[i_index].i_visible_lines *
426                     p_outpic->p[i_index].i_pitch;
427
428                 for( ; p_out < p_out_end ; )
429                 {
430                     uint8_t *p_line_end;
431
432                     p_out_end -= p_outpic->p[i_index].i_pitch
433                                   - p_outpic->p[i_index].i_visible_pitch;
434                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
435                         i_pitch;
436
437                     for( ; p_in < p_line_end ; )
438                     {
439                         p_line_end -= i_pitch;
440                         *(--p_out_end) = *p_line_end;
441                     }
442
443                     p_in++;
444                 }
445             }
446             break;
447
448         case TRANSFORM_MODE_180:
449             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
450             {
451                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
452                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
453                                             * p_pic->p[i_index].i_pitch;
454
455                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
456
457                 for( ; p_in < p_in_end ; )
458                 {
459                     uint8_t *p_line_start = p_in_end
460                                              - p_pic->p[i_index].i_pitch;
461                     p_in_end -= p_pic->p[i_index].i_pitch
462                                  - p_pic->p[i_index].i_visible_pitch;
463
464                     for( ; p_line_start < p_in_end ; )
465                     {
466                         *p_out++ = *(--p_in_end);
467                     }
468
469                     p_out += p_outpic->p[i_index].i_pitch
470                               - p_outpic->p[i_index].i_visible_pitch;
471                 }
472             }
473             break;
474
475         case TRANSFORM_MODE_270:
476             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
477             {
478                 int i_pitch = p_pic->p[i_index].i_pitch;
479
480                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
481
482                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
483                 uint8_t *p_out_end = p_out +
484                     p_outpic->p[i_index].i_visible_lines *
485                     p_outpic->p[i_index].i_pitch;
486
487                 for( ; p_out < p_out_end ; )
488                 {
489                     uint8_t *p_in_end;
490
491                     p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
492                         i_pitch;
493
494                     for( ; p_in < p_in_end ; )
495                     {
496                         p_in_end -= i_pitch;
497                         *p_out++ = *p_in_end;
498                     }
499
500                     p_out += p_outpic->p[i_index].i_pitch
501                               - p_outpic->p[i_index].i_visible_pitch;
502                     p_in++;
503                 }
504             }
505             break;
506
507         case TRANSFORM_MODE_HFLIP:
508             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
509             {
510                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
511                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
512                                             * p_pic->p[i_index].i_pitch;
513
514                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
515
516                 for( ; p_in < p_in_end ; )
517                 {
518                     p_in_end -= p_pic->p[i_index].i_pitch;
519                     vlc_memcpy( p_out, p_in_end,
520                                 p_pic->p[i_index].i_visible_pitch );
521                     p_out += p_pic->p[i_index].i_pitch;
522                 }
523             }
524             break;
525
526         case TRANSFORM_MODE_VFLIP:
527             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
528             {
529                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
530                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
531                                          * p_pic->p[i_index].i_pitch;
532
533                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
534
535                 for( ; p_in < p_in_end ; )
536                 {
537                     uint8_t *p_line_end = p_in
538                                         + p_pic->p[i_index].i_visible_pitch;
539
540                     for( ; p_in < p_line_end ; )
541                     {
542                         *p_out++ = *(--p_line_end);
543                     }
544
545                     p_in += p_pic->p[i_index].i_pitch;
546                 }
547             }
548             break;
549
550         default:
551             break;
552     }
553 }
554
555 static void FilterI422( vout_thread_t *p_vout,
556                         const picture_t *p_pic, picture_t *p_outpic )
557 {
558     int i_index;
559     switch( p_vout->p_sys->i_mode )
560     {
561         case TRANSFORM_MODE_180:
562         case TRANSFORM_MODE_HFLIP:
563         case TRANSFORM_MODE_VFLIP:
564             /* Fall back on the default implementation */
565             FilterPlanar( p_vout, p_pic, p_outpic );
566             return;
567
568         case TRANSFORM_MODE_90:
569             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
570             {
571                 int i_pitch = p_pic->p[i_index].i_pitch;
572
573                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
574
575                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
576                 uint8_t *p_out_end = p_out +
577                     p_outpic->p[i_index].i_visible_lines *
578                     p_outpic->p[i_index].i_pitch;
579
580                 if( i_index == 0 )
581                 {
582                     for( ; p_out < p_out_end ; )
583                     {
584                         uint8_t *p_line_end;
585
586                         p_out_end -= p_outpic->p[i_index].i_pitch
587                                       - p_outpic->p[i_index].i_visible_pitch;
588                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
589                             i_pitch;
590
591                         for( ; p_in < p_line_end ; )
592                         {
593                             p_line_end -= i_pitch;
594                             *(--p_out_end) = *p_line_end;
595                         }
596
597                         p_in++;
598                     }
599                 }
600                 else /* i_index == 1 or 2 */
601                 {
602                     for( ; p_out < p_out_end ; )
603                     {
604                         uint8_t *p_line_end, *p_out_end2;
605
606                         p_out_end -= p_outpic->p[i_index].i_pitch
607                                       - p_outpic->p[i_index].i_visible_pitch;
608                         p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
609                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
610                             i_pitch;
611
612                         for( ; p_in < p_line_end ; )
613                         {
614                             uint8_t p1, p2;
615
616                             p_line_end -= i_pitch;
617                             p1 = *p_line_end;
618                             p_line_end -= i_pitch;
619                             p2 = *p_line_end;
620
621                             /* Trick for (x+y)/2 without overflow, based on
622                              *   x + y == (x ^ y) + 2 * (x & y) */
623                             *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
624                             *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
625                         }
626
627                         p_out_end = p_out_end2;
628                         p_in++;
629                     }
630                 }
631             }
632             break;
633
634         case TRANSFORM_MODE_270:
635             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
636             {
637                 int i_pitch = p_pic->p[i_index].i_pitch;
638
639                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
640
641                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
642                 uint8_t *p_out_end = p_out +
643                     p_outpic->p[i_index].i_visible_lines *
644                     p_outpic->p[i_index].i_pitch;
645
646                 if( i_index == 0 )
647                 {
648                     for( ; p_out < p_out_end ; )
649                     {
650                         uint8_t *p_in_end;
651
652                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
653                             i_pitch;
654
655                         for( ; p_in < p_in_end ; )
656                         {
657                             p_in_end -= i_pitch;
658                             *p_out++ = *p_in_end;
659                         }
660
661                         p_out += p_outpic->p[i_index].i_pitch
662                                   - p_outpic->p[i_index].i_visible_pitch;
663                         p_in++;
664                     }
665                 }
666                 else /* i_index == 1 or 2 */
667                 {
668                     for( ; p_out < p_out_end ; )
669                     {
670                         uint8_t *p_in_end, *p_out2;
671
672                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
673                             i_pitch;
674                         p_out2 = p_out + p_outpic->p[i_index].i_pitch;
675
676                         for( ; p_in < p_in_end ; )
677                         {
678                             uint8_t p1, p2;
679
680                             p_in_end -= i_pitch;
681                             p1 = *p_in_end;
682                             p_in_end -= i_pitch;
683                             p2 = *p_in_end;
684
685                             /* Trick for (x+y)/2 without overflow, based on
686                              *   x + y == (x ^ y) + 2 * (x & y) */
687                             *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
688                             *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
689                         }
690
691                         p_out2 += p_outpic->p[i_index].i_pitch
692                                    - p_outpic->p[i_index].i_visible_pitch;
693                         p_out = p_out2;
694                         p_in++;
695                     }
696                 }
697             }
698             break;
699
700         default:
701             break;
702     }
703 }
704
705 static void FilterYUYV( vout_thread_t *p_vout,
706                         const picture_t *p_pic, picture_t *p_outpic )
707 {
708     int i_index;
709     int i_y_offset, i_u_offset, i_v_offset;
710     if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
711                              &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
712         return;
713
714     switch( p_vout->p_sys->i_mode )
715     {
716         case TRANSFORM_MODE_HFLIP:
717             /* Fall back on the default implementation */
718             FilterPlanar( p_vout, p_pic, p_outpic );
719             return;
720
721         case TRANSFORM_MODE_90:
722             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
723             {
724                 int i_pitch = p_pic->p[i_index].i_pitch;
725
726                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
727
728                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
729                 uint8_t *p_out_end = p_out +
730                     p_outpic->p[i_index].i_visible_lines *
731                     p_outpic->p[i_index].i_pitch;
732
733                 int i_offset  = i_u_offset;
734                 int i_offset2 = i_v_offset;
735                 for( ; p_out < p_out_end ; )
736                 {
737                     uint8_t *p_line_end;
738
739                     p_out_end -= p_outpic->p[i_index].i_pitch
740                                   - p_outpic->p[i_index].i_visible_pitch;
741                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
742                         i_pitch;
743
744                     for( ; p_in < p_line_end ; )
745                     {
746                         p_line_end -= i_pitch;
747                         p_out_end -= 4;
748                         p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
749                         p_out_end[i_u_offset] = p_line_end[i_offset];
750                         p_line_end -= i_pitch;
751                         p_out_end[i_y_offset] = p_line_end[i_y_offset];
752                         p_out_end[i_v_offset] = p_line_end[i_offset2];
753                     }
754
755                     p_in += 2;
756
757                     {
758                         int a = i_offset;
759                         i_offset = i_offset2;
760                         i_offset2 = a;
761                     }
762                 }
763             }
764             break;
765
766         case TRANSFORM_MODE_180:
767             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
768             {
769                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
770                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
771                                             * p_pic->p[i_index].i_pitch;
772
773                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
774
775                 for( ; p_in < p_in_end ; )
776                 {
777                     uint8_t *p_line_start = p_in_end
778                                              - p_pic->p[i_index].i_pitch;
779                     p_in_end -= p_pic->p[i_index].i_pitch
780                                  - p_pic->p[i_index].i_visible_pitch;
781
782                     for( ; p_line_start < p_in_end ; )
783                     {
784                         p_in_end -= 4;
785                         p_out[i_y_offset] = p_in_end[i_y_offset+2];
786                         p_out[i_u_offset] = p_in_end[i_u_offset];
787                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
788                         p_out[i_v_offset] = p_in_end[i_v_offset];
789                         p_out += 4;
790                     }
791
792                     p_out += p_outpic->p[i_index].i_pitch
793                               - p_outpic->p[i_index].i_visible_pitch;
794                 }
795             }
796             break;
797
798         case TRANSFORM_MODE_270:
799             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
800             {
801                 int i_pitch = p_pic->p[i_index].i_pitch;
802
803                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
804
805                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
806                 uint8_t *p_out_end = p_out +
807                     p_outpic->p[i_index].i_visible_lines *
808                     p_outpic->p[i_index].i_pitch;
809
810                 int i_offset  = i_u_offset;
811                 int i_offset2 = i_v_offset;
812                 for( ; p_out < p_out_end ; )
813                 {
814                     uint8_t *p_in_end;
815
816                     p_in_end = p_in
817                              + p_pic->p[i_index].i_visible_lines * i_pitch;
818
819                     for( ; p_in < p_in_end ; )
820                     {
821                         p_in_end -= i_pitch;
822                         p_out[i_y_offset] = p_in_end[i_y_offset];
823                         p_out[i_u_offset] = p_in_end[i_offset];
824                         p_in_end -= i_pitch;
825                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
826                         p_out[i_v_offset] = p_in_end[i_offset2];
827                         p_out += 4;
828                     }
829
830                     p_out += p_outpic->p[i_index].i_pitch
831                            - p_outpic->p[i_index].i_visible_pitch;
832                     p_in += 2;
833
834                     {
835                         int a = i_offset;
836                         i_offset = i_offset2;
837                         i_offset2 = a;
838                     }
839                 }
840             }
841             break;
842
843         case TRANSFORM_MODE_VFLIP:
844             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
845             {
846                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
847                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
848                                          * p_pic->p[i_index].i_pitch;
849
850                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
851
852                 for( ; p_in < p_in_end ; )
853                 {
854                     uint8_t *p_line_end = p_in
855                                         + p_pic->p[i_index].i_visible_pitch;
856
857                     for( ; p_in < p_line_end ; )
858                     {
859                         p_line_end -= 4;
860                         p_out[i_y_offset] = p_line_end[i_y_offset+2];
861                         p_out[i_u_offset] = p_line_end[i_u_offset];
862                         p_out[i_y_offset+2] = p_line_end[i_y_offset];
863                         p_out[i_v_offset] = p_line_end[i_v_offset];
864                         p_out += 4;
865                     }
866
867                     p_in += p_pic->p[i_index].i_pitch;
868                 }
869             }
870             break;
871
872         default:
873             break;
874     }
875 }