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