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