transform: add transposition and anti-transposition transforms
[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  * Copyright (C) 2010 Laurent Aimar
6  * Copyright (C) 2012 RĂ©mi Denis-Courmont
7  *
8  * Authors: Samuel Hocevar <sam@zoy.org>
9  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 #   include "config.h"
32 #endif
33 #include <limits.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
44
45 #define CFG_PREFIX "transform-"
46
47 #define TYPE_TEXT N_("Transform type")
48 static const char * const type_list[] = { "90", "180", "270",
49     "hflip", "vflip", "transpose", "antitranspose" };
50 static const char * const type_list_text[] = { N_("Rotate by 90 degrees"),
51     N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
52     N_("Flip horizontally"), N_("Flip vertically"),
53     N_("Transpose"), N_("Anti-transpose") };
54
55 vlc_module_begin()
56     set_description(N_("Video transformation filter"))
57     set_shortname(N_("Transformation"))
58     set_help(N_("Rotate or flip the video"))
59     set_capability("video filter2", 0)
60     set_category(CAT_VIDEO)
61     set_subcategory(SUBCAT_VIDEO_VFILTER)
62
63     add_string(CFG_PREFIX "type", "90", TYPE_TEXT, TYPE_TEXT, false)
64         change_string_list(type_list, type_list_text, 0)
65
66     add_shortcut("transform")
67     set_callbacks(Open, Close)
68 vlc_module_end()
69
70 /*****************************************************************************
71  * Local prototypes
72  *****************************************************************************/
73 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
74 {
75     VLC_UNUSED( h );
76     *sx = w - 1 - dx;
77     *sy = dy;
78 }
79 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
80 {
81     VLC_UNUSED( w );
82     *sx = dx;
83     *sy = h - 1 - dy;
84 }
85 static void Transpose(int *sx, int *sy, int w, int h, int dx, int dy)
86 {
87     VLC_UNUSED( h ); VLC_UNUSED( w );
88     *sx = dy;
89     *sy = dx;
90 }
91 static void AntiTranspose(int *sx, int *sy, int w, int h, int dx, int dy)
92 {
93     *sx = h - 1 - dy;
94     *sy = w - 1 - dx;
95 }
96 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
97 {
98     VLC_UNUSED( h );
99     *sx = dy;
100     *sy = w - 1 - dx;
101 }
102 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
103 {
104     *sx = w - dx;
105     *sy = h - dy;
106 }
107 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
108 {
109     VLC_UNUSED( w );
110     *sx = h - 1 - dy;
111     *sy = dx;
112 }
113 typedef void (*convert_t)(int *, int *, int, int, int, int);
114
115 #define PLANAR(f) \
116 static void Plane8_##f(plane_t *restrict dst, const plane_t *restrict src) \
117 { \
118     for (int y = 0; y < dst->i_visible_lines; y++) { \
119         for (int x = 0; x < dst->i_visible_pitch; x++) { \
120             int sx, sy; \
121             (f)(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines, x, y); \
122             dst->p_pixels[y * dst->i_pitch + x] = \
123                 src->p_pixels[sy * src->i_pitch + sx]; \
124         } \
125     } \
126 } \
127  \
128 static void Plane16_##f(plane_t *restrict dst, const plane_t *restrict src) \
129 { \
130     const uint16_t *src_pixels = (const uint16_t *)src->p_pixels; \
131     uint16_t *restrict dst_pixels = (uint16_t *)dst->p_pixels; \
132     unsigned src_pitch = src->i_pitch / 2; \
133     unsigned dst_pitch = dst->i_pitch / 2; \
134     unsigned dst_visible_width = dst->i_visible_pitch / 2; \
135  \
136     for (int y = 0; y < dst->i_visible_lines; y++) { \
137         for (unsigned x = 0; x < dst_visible_width; x++) { \
138             int sx, sy; \
139             (f)(&sx, &sy, dst_visible_width, dst->i_visible_lines, x, y);\
140             dst_pixels[y * dst_pitch + x] = \
141                 src_pixels[sy * src_pitch + sx]; \
142         } \
143     } \
144 } \
145  \
146 static void Plane32_##f(plane_t *restrict dst, const plane_t *restrict src) \
147 { \
148     const uint32_t *src_pixels = (const uint32_t *)src->p_pixels; \
149     uint32_t *restrict dst_pixels = (uint32_t *)dst->p_pixels; \
150     unsigned src_pitch = src->i_pitch / 4; \
151     unsigned dst_pitch = dst->i_pitch / 4; \
152     unsigned dst_visible_width = dst->i_visible_pitch / 4; \
153  \
154     for (int y = 0; y < dst->i_visible_lines; y++) { \
155         for (unsigned x = 0; x < dst_visible_width; x++) { \
156             int sx, sy; \
157             (f)(&sx, &sy, dst_visible_width, dst->i_visible_lines, x, y);\
158             dst_pixels[y * dst_pitch + x] = \
159                 src_pixels[sy * src_pitch + sx]; \
160         } \
161     } \
162 }
163
164 PLANAR(HFlip)
165 PLANAR(VFlip)
166 PLANAR(Transpose)
167 PLANAR(AntiTranspose)
168 PLANAR(R90)
169 PLANAR(R180)
170 PLANAR(R270)
171
172 typedef struct {
173     char      name[16];
174     bool      is_rotated;
175     convert_t convert;
176     convert_t iconvert;
177     void      (*plane8) (plane_t *dst, const plane_t *src);
178     void      (*plane16)(plane_t *dst, const plane_t *src);
179     void      (*plane32)(plane_t *dst, const plane_t *src);
180 } transform_description_t;
181
182 #define DESC(str, rotated, f, invf) \
183     { str, rotated, f, invf, Plane8_##f, Plane16_##f, Plane32_##f }
184
185 static const transform_description_t descriptions[] = {
186     DESC("90",            true,  R90,           R270),
187     DESC("180",           false, R180,          R180),
188     DESC("270",           true,  R270,          R90),
189     DESC("hflip",         false, HFlip,         HFlip),
190     DESC("vflip",         false, VFlip,         VFlip),
191     DESC("transpose",     true,  Transpose,     Transpose),
192     DESC("antitranspose", true,  AntiTranspose, AntiTranspose),
193 };
194
195 static const size_t n_transforms =
196     sizeof (descriptions) / sizeof (descriptions[0]);
197
198 struct filter_sys_t {
199     const vlc_chroma_description_t *chroma;
200     void (*plane)(plane_t *, const plane_t *);
201     convert_t convert;
202 };
203
204 static picture_t *Filter(filter_t *filter, picture_t *src)
205 {
206     filter_sys_t *sys = filter->p_sys;
207
208     picture_t *dst = filter_NewPicture(filter);
209     if (!dst) {
210         picture_Release(src);
211         return NULL;
212     }
213
214     const vlc_chroma_description_t *chroma = sys->chroma;
215     for (unsigned i = 0; i < chroma->plane_count; i++)
216          sys->plane(&dst->p[i], &src->p[i]);
217
218     picture_CopyProperties(dst, src);
219     picture_Release(src);
220     return dst;
221 }
222
223 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
224                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
225 {
226     VLC_UNUSED( mold );
227
228     const video_format_t *fmt = &filter->fmt_out.video;
229     const filter_sys_t   *sys = filter->p_sys;
230
231     *mouse = *mnew;
232     sys->convert(&mouse->i_x, &mouse->i_y,
233                  fmt->i_visible_width, fmt->i_visible_height,
234                  mouse->i_x, mouse->i_y);
235     return VLC_SUCCESS;
236 }
237
238 static int Open(vlc_object_t *object)
239 {
240     filter_t *filter = (filter_t *)object;
241     const video_format_t *src = &filter->fmt_in.video;
242     video_format_t       *dst = &filter->fmt_out.video;
243
244     const vlc_chroma_description_t *chroma =
245         vlc_fourcc_GetChromaDescription(src->i_chroma);
246     if (chroma == NULL)
247         return VLC_EGENERIC;
248
249     filter_sys_t *sys = malloc(sizeof(*sys));
250     if (!sys)
251         return VLC_ENOMEM;
252
253     sys->chroma = chroma;
254
255     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
256     const transform_description_t *dsc = NULL;
257
258     for (size_t i = 0; i < n_transforms; i++)
259         if (type_name && !strcmp(descriptions[i].name, type_name)) {
260             dsc = &descriptions[i];
261             break;
262         }
263     if (dsc == NULL) {
264         dsc = &descriptions[0];
265         msg_Warn(filter, "No valid transform mode provided, using '%s'",
266                  dsc->name);
267     }
268
269     free(type_name);
270
271     switch (chroma->pixel_size) {
272         case 1:
273             sys->plane = dsc->plane8;
274             break;
275         case 2:
276             sys->plane = dsc->plane16;
277             break;
278         case 4:
279             sys->plane = dsc->plane32;
280             break;
281         default:
282             msg_Err(filter, "Unsupported pixel size %u (chroma %4.4s)",
283                     chroma->pixel_size, (char *)&src->i_chroma);
284             goto error;
285     }
286
287     sys->convert = dsc->convert;
288     if (dsc->is_rotated) {
289         for (unsigned i = 0; i < chroma->plane_count; i++) {
290             if (chroma->p[i].w.num * chroma->p[i].h.den
291              != chroma->p[i].h.num * chroma->p[i].w.den) {
292                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
293                         (char *)&src->i_chroma); /* I422... */
294                 goto error;
295             }
296         }
297
298         if (!filter->b_allow_fmt_out_change) {
299             msg_Err(filter, "Format change is not allowed");
300             goto error;
301         }
302
303         dst->i_width          = src->i_height;
304         dst->i_visible_width  = src->i_visible_height;
305         dst->i_height         = src->i_width;
306         dst->i_visible_height = src->i_visible_width;
307         dst->i_sar_num        = src->i_sar_den;
308         dst->i_sar_den        = src->i_sar_num;
309     }
310
311     /* Deal with weird packed formats */
312     switch (src->i_chroma) {
313         case VLC_CODEC_YUYV:
314         case VLC_CODEC_UYVY:
315         case VLC_CODEC_YVYU:
316         case VLC_CODEC_VYUY:
317             if (dsc->is_rotated) {
318                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
319                         (char *)&src->i_chroma);
320                 goto error;
321             }
322             sys->plane = dsc->plane32; /* 32-bits, not 16-bits! */
323             break;
324         case VLC_CODEC_NV12:
325         case VLC_CODEC_NV21:
326             goto error;
327     }
328
329     dst->i_x_offset       = INT_MAX;
330     dst->i_y_offset       = INT_MAX;
331     for (int i = 0; i < 2; i++) {
332         int tx, ty;
333         dsc->iconvert(&tx, &ty, src->i_width, src->i_height,
334                       src->i_x_offset + i * (src->i_visible_width  - 1),
335                       src->i_y_offset + i * (src->i_visible_height - 1));
336         dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
337         dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
338     }
339
340     filter->p_sys           = sys;
341     filter->pf_video_filter = Filter;
342     filter->pf_video_mouse  = Mouse;
343     return VLC_SUCCESS;
344 error:
345     free(sys);
346     return VLC_EGENERIC;
347 }
348
349 static void Close(vlc_object_t *object)
350 {
351     filter_t     *filter = (filter_t *)object;
352     filter_sys_t *sys    = filter->p_sys;
353
354     free(sys);
355 }
356
357 #if 0
358 static void FilterI422( vout_thread_t *p_vout,
359                         const picture_t *p_pic, picture_t *p_outpic )
360 {
361     int i_index;
362     switch( p_vout->p_sys->i_mode )
363     {
364         case TRANSFORM_MODE_180:
365         case TRANSFORM_MODE_HFLIP:
366         case TRANSFORM_MODE_VFLIP:
367             /* Fall back on the default implementation */
368             FilterPlanar( p_vout, p_pic, p_outpic );
369             return;
370
371         case TRANSFORM_MODE_90:
372             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
373             {
374                 int i_pitch = p_pic->p[i_index].i_pitch;
375
376                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
377
378                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
379                 uint8_t *p_out_end = p_out +
380                     p_outpic->p[i_index].i_visible_lines *
381                     p_outpic->p[i_index].i_pitch;
382
383                 if( i_index == 0 )
384                 {
385                     for( ; p_out < p_out_end ; )
386                     {
387                         uint8_t *p_line_end;
388
389                         p_out_end -= p_outpic->p[i_index].i_pitch
390                                       - p_outpic->p[i_index].i_visible_pitch;
391                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
392                             i_pitch;
393
394                         for( ; p_in < p_line_end ; )
395                         {
396                             p_line_end -= i_pitch;
397                             *(--p_out_end) = *p_line_end;
398                         }
399
400                         p_in++;
401                     }
402                 }
403                 else /* i_index == 1 or 2 */
404                 {
405                     for( ; p_out < p_out_end ; )
406                     {
407                         uint8_t *p_line_end, *p_out_end2;
408
409                         p_out_end -= p_outpic->p[i_index].i_pitch
410                                       - p_outpic->p[i_index].i_visible_pitch;
411                         p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
412                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
413                             i_pitch;
414
415                         for( ; p_in < p_line_end ; )
416                         {
417                             uint8_t p1, p2;
418
419                             p_line_end -= i_pitch;
420                             p1 = *p_line_end;
421                             p_line_end -= i_pitch;
422                             p2 = *p_line_end;
423
424                             /* Trick for (x+y)/2 without overflow, based on
425                              *   x + y == (x ^ y) + 2 * (x & y) */
426                             *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
427                             *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
428                         }
429
430                         p_out_end = p_out_end2;
431                         p_in++;
432                     }
433                 }
434             }
435             break;
436
437         case TRANSFORM_MODE_270:
438             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
439             {
440                 int i_pitch = p_pic->p[i_index].i_pitch;
441
442                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
443
444                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
445                 uint8_t *p_out_end = p_out +
446                     p_outpic->p[i_index].i_visible_lines *
447                     p_outpic->p[i_index].i_pitch;
448
449                 if( i_index == 0 )
450                 {
451                     for( ; p_out < p_out_end ; )
452                     {
453                         uint8_t *p_in_end;
454
455                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
456                             i_pitch;
457
458                         for( ; p_in < p_in_end ; )
459                         {
460                             p_in_end -= i_pitch;
461                             *p_out++ = *p_in_end;
462                         }
463
464                         p_out += p_outpic->p[i_index].i_pitch
465                                   - p_outpic->p[i_index].i_visible_pitch;
466                         p_in++;
467                     }
468                 }
469                 else /* i_index == 1 or 2 */
470                 {
471                     for( ; p_out < p_out_end ; )
472                     {
473                         uint8_t *p_in_end, *p_out2;
474
475                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
476                             i_pitch;
477                         p_out2 = p_out + p_outpic->p[i_index].i_pitch;
478
479                         for( ; p_in < p_in_end ; )
480                         {
481                             uint8_t p1, p2;
482
483                             p_in_end -= i_pitch;
484                             p1 = *p_in_end;
485                             p_in_end -= i_pitch;
486                             p2 = *p_in_end;
487
488                             /* Trick for (x+y)/2 without overflow, based on
489                              *   x + y == (x ^ y) + 2 * (x & y) */
490                             *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
491                             *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
492                         }
493
494                         p_out2 += p_outpic->p[i_index].i_pitch
495                                    - p_outpic->p[i_index].i_visible_pitch;
496                         p_out = p_out2;
497                         p_in++;
498                     }
499                 }
500             }
501             break;
502
503         default:
504             break;
505     }
506 }
507
508 static void FilterYUYV( vout_thread_t *p_vout,
509                         const picture_t *p_pic, picture_t *p_outpic )
510 {
511     int i_index;
512     int i_y_offset, i_u_offset, i_v_offset;
513     if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
514                              &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
515         return;
516
517     switch( p_vout->p_sys->i_mode )
518     {
519         case TRANSFORM_MODE_VFLIP:
520             /* Fall back on the default implementation */
521             FilterPlanar( p_vout, p_pic, p_outpic );
522             return;
523
524         case TRANSFORM_MODE_90:
525             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
526             {
527                 int i_pitch = p_pic->p[i_index].i_pitch;
528
529                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
530
531                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
532                 uint8_t *p_out_end = p_out +
533                     p_outpic->p[i_index].i_visible_lines *
534                     p_outpic->p[i_index].i_pitch;
535
536                 int i_offset  = i_u_offset;
537                 int i_offset2 = i_v_offset;
538                 for( ; p_out < p_out_end ; )
539                 {
540                     uint8_t *p_line_end;
541
542                     p_out_end -= p_outpic->p[i_index].i_pitch
543                                   - p_outpic->p[i_index].i_visible_pitch;
544                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
545                         i_pitch;
546
547                     for( ; p_in < p_line_end ; )
548                     {
549                         p_line_end -= i_pitch;
550                         p_out_end -= 4;
551                         p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
552                         p_out_end[i_u_offset] = p_line_end[i_offset];
553                         p_line_end -= i_pitch;
554                         p_out_end[i_y_offset] = p_line_end[i_y_offset];
555                         p_out_end[i_v_offset] = p_line_end[i_offset2];
556                     }
557
558                     p_in += 2;
559
560                     {
561                         int a = i_offset;
562                         i_offset = i_offset2;
563                         i_offset2 = a;
564                     }
565                 }
566             }
567             break;
568
569         case TRANSFORM_MODE_180:
570             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
571             {
572                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
573                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
574                                             * p_pic->p[i_index].i_pitch;
575
576                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
577
578                 for( ; p_in < p_in_end ; )
579                 {
580                     uint8_t *p_line_start = p_in_end
581                                              - p_pic->p[i_index].i_pitch;
582                     p_in_end -= p_pic->p[i_index].i_pitch
583                                  - p_pic->p[i_index].i_visible_pitch;
584
585                     for( ; p_line_start < p_in_end ; )
586                     {
587                         p_in_end -= 4;
588                         p_out[i_y_offset] = p_in_end[i_y_offset+2];
589                         p_out[i_u_offset] = p_in_end[i_u_offset];
590                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
591                         p_out[i_v_offset] = p_in_end[i_v_offset];
592                         p_out += 4;
593                     }
594
595                     p_out += p_outpic->p[i_index].i_pitch
596                               - p_outpic->p[i_index].i_visible_pitch;
597                 }
598             }
599             break;
600
601         case TRANSFORM_MODE_270:
602             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
603             {
604                 int i_pitch = p_pic->p[i_index].i_pitch;
605
606                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
607
608                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
609                 uint8_t *p_out_end = p_out +
610                     p_outpic->p[i_index].i_visible_lines *
611                     p_outpic->p[i_index].i_pitch;
612
613                 int i_offset  = i_u_offset;
614                 int i_offset2 = i_v_offset;
615                 for( ; p_out < p_out_end ; )
616                 {
617                     uint8_t *p_in_end;
618
619                     p_in_end = p_in
620                              + p_pic->p[i_index].i_visible_lines * i_pitch;
621
622                     for( ; p_in < p_in_end ; )
623                     {
624                         p_in_end -= i_pitch;
625                         p_out[i_y_offset] = p_in_end[i_y_offset];
626                         p_out[i_u_offset] = p_in_end[i_offset];
627                         p_in_end -= i_pitch;
628                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
629                         p_out[i_v_offset] = p_in_end[i_offset2];
630                         p_out += 4;
631                     }
632
633                     p_out += p_outpic->p[i_index].i_pitch
634                            - p_outpic->p[i_index].i_visible_pitch;
635                     p_in += 2;
636
637                     {
638                         int a = i_offset;
639                         i_offset = i_offset2;
640                         i_offset2 = a;
641                     }
642                 }
643             }
644             break;
645
646         case TRANSFORM_MODE_HFLIP:
647             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
648             {
649                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
650                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
651                                          * p_pic->p[i_index].i_pitch;
652
653                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
654
655                 for( ; p_in < p_in_end ; )
656                 {
657                     uint8_t *p_line_end = p_in
658                                         + p_pic->p[i_index].i_visible_pitch;
659
660                     for( ; p_in < p_line_end ; )
661                     {
662                         p_line_end -= 4;
663                         p_out[i_y_offset] = p_line_end[i_y_offset+2];
664                         p_out[i_u_offset] = p_line_end[i_u_offset];
665                         p_out[i_y_offset+2] = p_line_end[i_y_offset];
666                         p_out[i_v_offset] = p_line_end[i_v_offset];
667                         p_out += 4;
668                     }
669
670                     p_in += p_pic->p[i_index].i_pitch;
671                     p_out += p_outpic->p[i_index].i_pitch
672                                 - p_outpic->p[i_index].i_visible_pitch;
673                 }
674             }
675             break;
676
677         default:
678             break;
679     }
680 }
681 #endif