transform: do not repeat choices in long help
[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  * $Id$
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", "hflip", "vflip" };
49 static const char * const type_list_text[] = { N_("Rotate by 90 degrees"),
50   N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
51   N_("Flip horizontally"), N_("Flip vertically") };
52
53 vlc_module_begin()
54     set_description(N_("Video transformation filter"))
55     set_shortname(N_("Transformation"))
56     set_help(N_("Rotate or flip the video"))
57     set_capability("video filter2", 0)
58     set_category(CAT_VIDEO)
59     set_subcategory(SUBCAT_VIDEO_VFILTER)
60
61     add_string(CFG_PREFIX "type", "90", TYPE_TEXT, TYPE_TEXT, false)
62         change_string_list(type_list, type_list_text, 0)
63
64     add_shortcut("transform")
65     set_callbacks(Open, Close)
66 vlc_module_end()
67
68 /*****************************************************************************
69  * Local prototypes
70  *****************************************************************************/
71 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
72 {
73     VLC_UNUSED( h );
74     *sx = w - 1 - dx;
75     *sy = dy;
76 }
77 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
78 {
79     VLC_UNUSED( w );
80     *sx = dx;
81     *sy = h - 1 - dy;
82 }
83 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
84 {
85     VLC_UNUSED( h );
86     *sx = dy;
87     *sy = w - 1 - dx;
88 }
89 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
90 {
91     *sx = w - dx;
92     *sy = h - dy;
93 }
94 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
95 {
96     VLC_UNUSED( w );
97     *sx = h - 1 - dy;
98     *sy = dx;
99 }
100 typedef void (*convert_t)(int *, int *, int, int, int, int);
101
102 #define PLANAR(f) \
103 static void Planar##f(plane_t *restrict dst, const plane_t *restrict src) \
104 { \
105     for (int y = 0; y < dst->i_visible_lines; y++) { \
106         for (int x = 0; x < dst->i_visible_pitch; x++) { \
107             int sx, sy; \
108             (f)(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines, x, y); \
109             dst->p_pixels[y * dst->i_pitch + x] = \
110                 src->p_pixels[sy * src->i_pitch + sx]; \
111         } \
112     } \
113 }
114
115 PLANAR(HFlip)
116 PLANAR(VFlip)
117 PLANAR(R90)
118 PLANAR(R180)
119 PLANAR(R270)
120
121 typedef struct {
122     char      name[8];
123     bool      is_rotated;
124     convert_t convert;
125     convert_t iconvert;
126     void      (*planar)(plane_t *dst, const plane_t *src);
127 } transform_description_t;
128
129 static const transform_description_t descriptions[] = {
130     { "90",    true,  R90,   R270,  PlanarR90, },
131     { "180",   false, R180,  R180,  PlanarR180, },
132     { "270",   true,  R270,  R90,   PlanarR270, },
133     { "hflip", false, HFlip, HFlip, PlanarHFlip, },
134     { "vflip", false, VFlip, VFlip, PlanarVFlip, },
135
136     { "", false, NULL, NULL, NULL, }
137 };
138
139 struct filter_sys_t {
140     const transform_description_t  *dsc;
141     const vlc_chroma_description_t *chroma;
142 };
143
144 static picture_t *Filter(filter_t *filter, picture_t *src)
145 {
146     filter_sys_t *sys = filter->p_sys;
147
148     picture_t *dst = filter_NewPicture(filter);
149     if (!dst) {
150         picture_Release(src);
151         return NULL;
152     }
153
154     const vlc_chroma_description_t *chroma = sys->chroma;
155     if (chroma->plane_count < 3) {
156         /* TODO */
157     } else {
158         for (unsigned i = 0; i < chroma->plane_count; i++)
159             sys->dsc->planar(&dst->p[i], &src->p[i]);
160     }
161
162     picture_CopyProperties(dst, src);
163     picture_Release(src);
164     return dst;
165 }
166
167 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
168                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
169 {
170     VLC_UNUSED( mold );
171
172     const video_format_t          *fmt = &filter->fmt_out.video;
173     const transform_description_t *dsc = filter->p_sys->dsc;
174
175     *mouse = *mnew;
176     dsc->convert(&mouse->i_x, &mouse->i_y,
177                  fmt->i_visible_width, fmt->i_visible_height, mouse->i_x, mouse->i_y);
178     return VLC_SUCCESS;
179 }
180
181 static bool SupportedChroma(const vlc_chroma_description_t *chroma)
182 {
183     if (chroma == NULL)
184         return false;
185
186     if (chroma->pixel_size != 1)
187         return false;
188
189     for (unsigned i = 0; i < chroma->plane_count; i++)
190         if (chroma->p[i].w.num * chroma->p[i].h.den
191          != chroma->p[i].h.num * chroma->p[i].w.den)
192             return false;
193
194     return true;
195 }
196
197 static int Open(vlc_object_t *object)
198 {
199     filter_t *filter = (filter_t *)object;
200     const video_format_t *src = &filter->fmt_in.video;
201     video_format_t       *dst = &filter->fmt_out.video;
202
203     const vlc_chroma_description_t *chroma =
204         vlc_fourcc_GetChromaDescription(src->i_chroma);
205     if (!SupportedChroma(chroma)) {
206         msg_Err(filter, "Unsupported chroma (%4.4s)", (char*)&src->i_chroma);
207         /* TODO support packed and rgb */
208         return VLC_EGENERIC;
209     }
210
211     filter_sys_t *sys = malloc(sizeof(*sys));
212     if (!sys)
213         return VLC_ENOMEM;
214
215     sys->chroma = chroma;
216
217     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
218
219     sys->dsc = NULL;
220     for (int i = 0; !sys->dsc && *descriptions[i].name; i++) {
221         if (type_name && *type_name && !strcmp(descriptions[i].name, type_name))
222             sys->dsc = &descriptions[i];
223     }
224     if (!sys->dsc) {
225         sys->dsc = &descriptions[0];
226         msg_Warn(filter, "No valid transform mode provided, using '%s'", sys->dsc->name);
227     }
228
229     free(type_name);
230
231     if (sys->dsc->is_rotated) {
232         if (!filter->b_allow_fmt_out_change) {
233             msg_Err(filter, "Format change is not allowed");
234             free(sys);
235             return VLC_EGENERIC;
236         }
237
238         dst->i_width          = src->i_height;
239         dst->i_visible_width  = src->i_visible_height;
240         dst->i_height         = src->i_width;
241         dst->i_visible_height = src->i_visible_width;
242         dst->i_sar_num        = src->i_sar_den;
243         dst->i_sar_den        = src->i_sar_num;
244     }
245
246     dst->i_x_offset       = INT_MAX;
247     dst->i_y_offset       = INT_MAX;
248     for (int i = 0; i < 2; i++) {
249         int tx, ty;
250         sys->dsc->iconvert(&tx, &ty,
251                            src->i_width, src->i_height,
252                            src->i_x_offset + i * (src->i_visible_width  - 1),
253                            src->i_y_offset + i * (src->i_visible_height - 1));
254         dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
255         dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
256     }
257
258     filter->p_sys           = sys;
259     filter->pf_video_filter = Filter;
260     filter->pf_video_mouse  = Mouse;
261     return VLC_SUCCESS;
262 }
263
264 static void Close(vlc_object_t *object)
265 {
266     filter_t     *filter = (filter_t *)object;
267     filter_sys_t *sys    = filter->p_sys;
268
269     free(sys);
270 }
271
272 #if 0
273 static void FilterI422( vout_thread_t *p_vout,
274                         const picture_t *p_pic, picture_t *p_outpic )
275 {
276     int i_index;
277     switch( p_vout->p_sys->i_mode )
278     {
279         case TRANSFORM_MODE_180:
280         case TRANSFORM_MODE_HFLIP:
281         case TRANSFORM_MODE_VFLIP:
282             /* Fall back on the default implementation */
283             FilterPlanar( p_vout, p_pic, p_outpic );
284             return;
285
286         case TRANSFORM_MODE_90:
287             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
288             {
289                 int i_pitch = p_pic->p[i_index].i_pitch;
290
291                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
292
293                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
294                 uint8_t *p_out_end = p_out +
295                     p_outpic->p[i_index].i_visible_lines *
296                     p_outpic->p[i_index].i_pitch;
297
298                 if( i_index == 0 )
299                 {
300                     for( ; p_out < p_out_end ; )
301                     {
302                         uint8_t *p_line_end;
303
304                         p_out_end -= p_outpic->p[i_index].i_pitch
305                                       - p_outpic->p[i_index].i_visible_pitch;
306                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
307                             i_pitch;
308
309                         for( ; p_in < p_line_end ; )
310                         {
311                             p_line_end -= i_pitch;
312                             *(--p_out_end) = *p_line_end;
313                         }
314
315                         p_in++;
316                     }
317                 }
318                 else /* i_index == 1 or 2 */
319                 {
320                     for( ; p_out < p_out_end ; )
321                     {
322                         uint8_t *p_line_end, *p_out_end2;
323
324                         p_out_end -= p_outpic->p[i_index].i_pitch
325                                       - p_outpic->p[i_index].i_visible_pitch;
326                         p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
327                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
328                             i_pitch;
329
330                         for( ; p_in < p_line_end ; )
331                         {
332                             uint8_t p1, p2;
333
334                             p_line_end -= i_pitch;
335                             p1 = *p_line_end;
336                             p_line_end -= i_pitch;
337                             p2 = *p_line_end;
338
339                             /* Trick for (x+y)/2 without overflow, based on
340                              *   x + y == (x ^ y) + 2 * (x & y) */
341                             *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
342                             *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
343                         }
344
345                         p_out_end = p_out_end2;
346                         p_in++;
347                     }
348                 }
349             }
350             break;
351
352         case TRANSFORM_MODE_270:
353             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
354             {
355                 int i_pitch = p_pic->p[i_index].i_pitch;
356
357                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
358
359                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
360                 uint8_t *p_out_end = p_out +
361                     p_outpic->p[i_index].i_visible_lines *
362                     p_outpic->p[i_index].i_pitch;
363
364                 if( i_index == 0 )
365                 {
366                     for( ; p_out < p_out_end ; )
367                     {
368                         uint8_t *p_in_end;
369
370                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
371                             i_pitch;
372
373                         for( ; p_in < p_in_end ; )
374                         {
375                             p_in_end -= i_pitch;
376                             *p_out++ = *p_in_end;
377                         }
378
379                         p_out += p_outpic->p[i_index].i_pitch
380                                   - p_outpic->p[i_index].i_visible_pitch;
381                         p_in++;
382                     }
383                 }
384                 else /* i_index == 1 or 2 */
385                 {
386                     for( ; p_out < p_out_end ; )
387                     {
388                         uint8_t *p_in_end, *p_out2;
389
390                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
391                             i_pitch;
392                         p_out2 = p_out + p_outpic->p[i_index].i_pitch;
393
394                         for( ; p_in < p_in_end ; )
395                         {
396                             uint8_t p1, p2;
397
398                             p_in_end -= i_pitch;
399                             p1 = *p_in_end;
400                             p_in_end -= i_pitch;
401                             p2 = *p_in_end;
402
403                             /* Trick for (x+y)/2 without overflow, based on
404                              *   x + y == (x ^ y) + 2 * (x & y) */
405                             *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
406                             *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
407                         }
408
409                         p_out2 += p_outpic->p[i_index].i_pitch
410                                    - p_outpic->p[i_index].i_visible_pitch;
411                         p_out = p_out2;
412                         p_in++;
413                     }
414                 }
415             }
416             break;
417
418         default:
419             break;
420     }
421 }
422
423 static void FilterYUYV( vout_thread_t *p_vout,
424                         const picture_t *p_pic, picture_t *p_outpic )
425 {
426     int i_index;
427     int i_y_offset, i_u_offset, i_v_offset;
428     if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
429                              &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
430         return;
431
432     switch( p_vout->p_sys->i_mode )
433     {
434         case TRANSFORM_MODE_VFLIP:
435             /* Fall back on the default implementation */
436             FilterPlanar( p_vout, p_pic, p_outpic );
437             return;
438
439         case TRANSFORM_MODE_90:
440             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
441             {
442                 int i_pitch = p_pic->p[i_index].i_pitch;
443
444                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
445
446                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
447                 uint8_t *p_out_end = p_out +
448                     p_outpic->p[i_index].i_visible_lines *
449                     p_outpic->p[i_index].i_pitch;
450
451                 int i_offset  = i_u_offset;
452                 int i_offset2 = i_v_offset;
453                 for( ; p_out < p_out_end ; )
454                 {
455                     uint8_t *p_line_end;
456
457                     p_out_end -= p_outpic->p[i_index].i_pitch
458                                   - p_outpic->p[i_index].i_visible_pitch;
459                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
460                         i_pitch;
461
462                     for( ; p_in < p_line_end ; )
463                     {
464                         p_line_end -= i_pitch;
465                         p_out_end -= 4;
466                         p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
467                         p_out_end[i_u_offset] = p_line_end[i_offset];
468                         p_line_end -= i_pitch;
469                         p_out_end[i_y_offset] = p_line_end[i_y_offset];
470                         p_out_end[i_v_offset] = p_line_end[i_offset2];
471                     }
472
473                     p_in += 2;
474
475                     {
476                         int a = i_offset;
477                         i_offset = i_offset2;
478                         i_offset2 = a;
479                     }
480                 }
481             }
482             break;
483
484         case TRANSFORM_MODE_180:
485             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
486             {
487                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
488                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
489                                             * p_pic->p[i_index].i_pitch;
490
491                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
492
493                 for( ; p_in < p_in_end ; )
494                 {
495                     uint8_t *p_line_start = p_in_end
496                                              - p_pic->p[i_index].i_pitch;
497                     p_in_end -= p_pic->p[i_index].i_pitch
498                                  - p_pic->p[i_index].i_visible_pitch;
499
500                     for( ; p_line_start < p_in_end ; )
501                     {
502                         p_in_end -= 4;
503                         p_out[i_y_offset] = p_in_end[i_y_offset+2];
504                         p_out[i_u_offset] = p_in_end[i_u_offset];
505                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
506                         p_out[i_v_offset] = p_in_end[i_v_offset];
507                         p_out += 4;
508                     }
509
510                     p_out += p_outpic->p[i_index].i_pitch
511                               - p_outpic->p[i_index].i_visible_pitch;
512                 }
513             }
514             break;
515
516         case TRANSFORM_MODE_270:
517             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
518             {
519                 int i_pitch = p_pic->p[i_index].i_pitch;
520
521                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
522
523                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
524                 uint8_t *p_out_end = p_out +
525                     p_outpic->p[i_index].i_visible_lines *
526                     p_outpic->p[i_index].i_pitch;
527
528                 int i_offset  = i_u_offset;
529                 int i_offset2 = i_v_offset;
530                 for( ; p_out < p_out_end ; )
531                 {
532                     uint8_t *p_in_end;
533
534                     p_in_end = p_in
535                              + p_pic->p[i_index].i_visible_lines * i_pitch;
536
537                     for( ; p_in < p_in_end ; )
538                     {
539                         p_in_end -= i_pitch;
540                         p_out[i_y_offset] = p_in_end[i_y_offset];
541                         p_out[i_u_offset] = p_in_end[i_offset];
542                         p_in_end -= i_pitch;
543                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
544                         p_out[i_v_offset] = p_in_end[i_offset2];
545                         p_out += 4;
546                     }
547
548                     p_out += p_outpic->p[i_index].i_pitch
549                            - p_outpic->p[i_index].i_visible_pitch;
550                     p_in += 2;
551
552                     {
553                         int a = i_offset;
554                         i_offset = i_offset2;
555                         i_offset2 = a;
556                     }
557                 }
558             }
559             break;
560
561         case TRANSFORM_MODE_HFLIP:
562             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
563             {
564                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
565                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
566                                          * p_pic->p[i_index].i_pitch;
567
568                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
569
570                 for( ; p_in < p_in_end ; )
571                 {
572                     uint8_t *p_line_end = p_in
573                                         + p_pic->p[i_index].i_visible_pitch;
574
575                     for( ; p_in < p_line_end ; )
576                     {
577                         p_line_end -= 4;
578                         p_out[i_y_offset] = p_line_end[i_y_offset+2];
579                         p_out[i_u_offset] = p_line_end[i_u_offset];
580                         p_out[i_y_offset+2] = p_line_end[i_y_offset];
581                         p_out[i_v_offset] = p_line_end[i_v_offset];
582                         p_out += 4;
583                     }
584
585                     p_in += p_pic->p[i_index].i_pitch;
586                     p_out += p_outpic->p[i_index].i_pitch
587                                 - p_outpic->p[i_index].i_visible_pitch;
588                 }
589             }
590             break;
591
592         default:
593             break;
594     }
595 }
596 #endif