video_filter/transform.c: Horizontal and Vertical flip where swapped.
[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         change_safe()
66
67     add_shortcut("transform")
68     set_callbacks(Open, Close)
69 vlc_module_end()
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
75 {
76     VLC_UNUSED( h );
77     *sx = w - 1 - dx;
78     *sy = dy;
79 }
80 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
81 {
82     VLC_UNUSED( w );
83     *sx = dx;
84     *sy = h - 1 - dy;
85 }
86 static void Transpose(int *sx, int *sy, int w, int h, int dx, int dy)
87 {
88     VLC_UNUSED( h ); VLC_UNUSED( w );
89     *sx = dy;
90     *sy = dx;
91 }
92 static void AntiTranspose(int *sx, int *sy, int w, int h, int dx, int dy)
93 {
94     *sx = h - 1 - dy;
95     *sy = w - 1 - dx;
96 }
97 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
98 {
99     VLC_UNUSED( h );
100     *sx = dy;
101     *sy = w - 1 - dx;
102 }
103 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
104 {
105     *sx = w - dx;
106     *sy = h - dy;
107 }
108 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
109 {
110     VLC_UNUSED( w );
111     *sx = h - 1 - dy;
112     *sy = dx;
113 }
114 typedef void (*convert_t)(int *, int *, int, int, int, int);
115
116 #define PLANAR(f) \
117 static void Plane8_##f(plane_t *restrict dst, const plane_t *restrict src) \
118 { \
119     for (int y = 0; y < dst->i_visible_lines; y++) { \
120         for (int x = 0; x < dst->i_visible_pitch; x++) { \
121             int sx, sy; \
122             (f)(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines, x, y); \
123             dst->p_pixels[y * dst->i_pitch + x] = \
124                 src->p_pixels[sy * src->i_pitch + sx]; \
125         } \
126     } \
127 } \
128  \
129 static void Plane16_##f(plane_t *restrict dst, const plane_t *restrict src) \
130 { \
131     const uint16_t *src_pixels = (const uint16_t *)src->p_pixels; \
132     uint16_t *restrict dst_pixels = (uint16_t *)dst->p_pixels; \
133     unsigned src_width = src->i_pitch / 2; \
134     unsigned dst_width = dst->i_pitch / 2; \
135     unsigned dst_visible_width = dst->i_visible_pitch / 2; \
136  \
137     for (int y = 0; y < dst->i_visible_lines; y++) { \
138         for (unsigned x = 0; x < dst_visible_width; x++) { \
139             int sx, sy; \
140             (f)(&sx, &sy, dst_visible_width, dst->i_visible_lines, x, y); \
141             dst_pixels[y * dst_width + x] = \
142                 src_pixels[sy * src_width + sx]; \
143         } \
144     } \
145 } \
146  \
147 static void Plane32_##f(plane_t *restrict dst, const plane_t *restrict src) \
148 { \
149     const uint32_t *src_pixels = (const uint32_t *)src->p_pixels; \
150     uint32_t *restrict dst_pixels = (uint32_t *)dst->p_pixels; \
151     unsigned src_width = src->i_pitch / 4; \
152     unsigned dst_width = dst->i_pitch / 4; \
153     unsigned dst_visible_width = dst->i_visible_pitch / 4; \
154  \
155     for (int y = 0; y < dst->i_visible_lines; y++) { \
156         for (unsigned x = 0; x < dst_visible_width; x++) { \
157             int sx, sy; \
158             (f)(&sx, &sy, dst_visible_width, dst->i_visible_lines, x, y); \
159             dst_pixels[y * dst_width + x] = \
160                 src_pixels[sy * src_width + sx]; \
161         } \
162     } \
163 } \
164  \
165 static void YUYV_##f(plane_t *restrict dst, const plane_t *restrict src) \
166 { \
167     unsigned dst_visible_width = dst->i_visible_pitch / 2; \
168  \
169     for (int y = 0; y < dst->i_visible_lines; y += 2) { \
170         for (unsigned x = 0; x < dst_visible_width; x+= 2) { \
171             int sx0, sy0, sx1, sy1; \
172             (f)(&sx0, &sy0, dst_visible_width, dst->i_visible_lines, x, y); \
173             (f)(&sx1, &sy1, dst_visible_width, dst->i_visible_lines, \
174                 x + 1, y + 1); \
175             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * (x + 0)] = \
176                 src->p_pixels[sy0 * src->i_pitch + 2 * sx0]; \
177             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * (x + 1)] = \
178                 src->p_pixels[sy1 * src->i_pitch + 2 * sx0]; \
179             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * (x + 0)] = \
180                 src->p_pixels[sy0 * src->i_pitch + 2 * sx1]; \
181             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * (x + 1)] = \
182                 src->p_pixels[sy1 * src->i_pitch + 2 * sx1]; \
183  \
184             int sx, sy, u, v; \
185             (f)(&sx, &sy, dst_visible_width / 2, dst->i_visible_lines / 2, \
186                 x / 2, y / 2); \
187             u = (1 + src->p_pixels[2 * sy * src->i_pitch + 4 * sx + 1] + \
188                 src->p_pixels[(2 * sy + 1) * src->i_pitch + 4 * sx + 1]) / 2; \
189             v = (1 + src->p_pixels[2 * sy * src->i_pitch + 4 * sx + 3] + \
190                 src->p_pixels[(2 * sy + 1) * src->i_pitch + 4 * sx + 3]) / 2; \
191             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * x + 1] = u; \
192             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * x + 3] = v; \
193             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * x + 1] = u; \
194             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * x + 3] = v; \
195         } \
196     } \
197 }
198
199 PLANAR(HFlip)
200 PLANAR(VFlip)
201 PLANAR(Transpose)
202 PLANAR(AntiTranspose)
203 PLANAR(R90)
204 PLANAR(R180)
205 PLANAR(R270)
206
207 typedef struct {
208     char      name[16];
209     bool      is_rotated;
210     convert_t convert;
211     convert_t iconvert;
212     void      (*plane8) (plane_t *dst, const plane_t *src);
213     void      (*plane16)(plane_t *dst, const plane_t *src);
214     void      (*plane32)(plane_t *dst, const plane_t *src);
215     void      (*yuyv)(plane_t *dst, const plane_t *src);
216 } transform_description_t;
217
218 #define DESC(str, rotated, f, invf) \
219     { str, rotated, f, invf, Plane8_##f, Plane16_##f, Plane32_##f, YUYV_##f }
220
221 static const transform_description_t descriptions[] = {
222     DESC("90",            true,  R90,           R270),
223     DESC("180",           false, R180,          R180),
224     DESC("270",           true,  R270,          R90),
225     DESC("hflip",         false, HFlip,         HFlip),
226     DESC("vflip",         false, VFlip,         VFlip),
227     DESC("transpose",     true,  Transpose,     Transpose),
228     DESC("antitranspose", true,  AntiTranspose, AntiTranspose),
229 };
230
231 static const size_t n_transforms =
232     sizeof (descriptions) / sizeof (descriptions[0]);
233
234 struct filter_sys_t {
235     const vlc_chroma_description_t *chroma;
236     void (*plane)(plane_t *, const plane_t *);
237     convert_t convert;
238 };
239
240 static picture_t *Filter(filter_t *filter, picture_t *src)
241 {
242     filter_sys_t *sys = filter->p_sys;
243
244     picture_t *dst = filter_NewPicture(filter);
245     if (!dst) {
246         picture_Release(src);
247         return NULL;
248     }
249
250     const vlc_chroma_description_t *chroma = sys->chroma;
251     for (unsigned i = 0; i < chroma->plane_count; i++)
252          sys->plane(&dst->p[i], &src->p[i]);
253
254     picture_CopyProperties(dst, src);
255     picture_Release(src);
256     return dst;
257 }
258
259 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
260                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
261 {
262     VLC_UNUSED( mold );
263
264     const video_format_t *fmt = &filter->fmt_out.video;
265     const filter_sys_t   *sys = filter->p_sys;
266
267     *mouse = *mnew;
268     sys->convert(&mouse->i_x, &mouse->i_y,
269                  fmt->i_visible_width, fmt->i_visible_height,
270                  mouse->i_x, mouse->i_y);
271     return VLC_SUCCESS;
272 }
273
274 static int Open(vlc_object_t *object)
275 {
276     filter_t *filter = (filter_t *)object;
277     const video_format_t *src = &filter->fmt_in.video;
278     video_format_t       *dst = &filter->fmt_out.video;
279
280     const vlc_chroma_description_t *chroma =
281         vlc_fourcc_GetChromaDescription(src->i_chroma);
282     if (chroma == NULL)
283         return VLC_EGENERIC;
284
285     filter_sys_t *sys = malloc(sizeof(*sys));
286     if (!sys)
287         return VLC_ENOMEM;
288
289     sys->chroma = chroma;
290
291     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
292     const transform_description_t *dsc = NULL;
293
294     for (size_t i = 0; i < n_transforms; i++)
295         if (type_name && !strcmp(descriptions[i].name, type_name)) {
296             dsc = &descriptions[i];
297             break;
298         }
299     if (dsc == NULL) {
300         dsc = &descriptions[0];
301         msg_Warn(filter, "No valid transform mode provided, using '%s'",
302                  dsc->name);
303     }
304
305     free(type_name);
306
307     switch (chroma->pixel_size) {
308         case 1:
309             sys->plane = dsc->plane8;
310             break;
311         case 2:
312             sys->plane = dsc->plane16;
313             break;
314         case 4:
315             sys->plane = dsc->plane32;
316             break;
317         default:
318             msg_Err(filter, "Unsupported pixel size %u (chroma %4.4s)",
319                     chroma->pixel_size, (char *)&src->i_chroma);
320             goto error;
321     }
322
323     sys->convert = dsc->convert;
324     if (dsc->is_rotated) {
325         for (unsigned i = 0; i < chroma->plane_count; i++) {
326             if (chroma->p[i].w.num * chroma->p[i].h.den
327              != chroma->p[i].h.num * chroma->p[i].w.den) {
328                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
329                         (char *)&src->i_chroma); /* I422... */
330                 goto error;
331             }
332         }
333
334         if (!filter->b_allow_fmt_out_change) {
335             msg_Err(filter, "Format change is not allowed");
336             goto error;
337         }
338
339         dst->i_width          = src->i_height;
340         dst->i_visible_width  = src->i_visible_height;
341         dst->i_height         = src->i_width;
342         dst->i_visible_height = src->i_visible_width;
343         dst->i_sar_num        = src->i_sar_den;
344         dst->i_sar_den        = src->i_sar_num;
345     }
346
347     /* Deal with weird packed formats */
348     switch (src->i_chroma) {
349         case VLC_CODEC_YUYV:
350         case VLC_CODEC_YVYU:
351             sys->plane = dsc->is_rotated ? dsc->yuyv : dsc->plane32;
352             break;
353         case VLC_CODEC_UYVY:
354         case VLC_CODEC_VYUY:
355             if (dsc->is_rotated) {
356                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
357                         (char *)&src->i_chroma);
358                 goto error;
359             }
360             sys->plane = dsc->plane32; /* 32-bits, not 16-bits! */
361             break;
362         case VLC_CODEC_NV12:
363         case VLC_CODEC_NV21:
364             goto error;
365     }
366
367     dst->i_x_offset       = INT_MAX;
368     dst->i_y_offset       = INT_MAX;
369     for (int i = 0; i < 2; i++) {
370         int tx, ty;
371         dsc->iconvert(&tx, &ty, src->i_width, src->i_height,
372                       src->i_x_offset + i * (src->i_visible_width  - 1),
373                       src->i_y_offset + i * (src->i_visible_height - 1));
374         dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
375         dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
376     }
377
378     filter->p_sys           = sys;
379     filter->pf_video_filter = Filter;
380     filter->pf_video_mouse  = Mouse;
381     return VLC_SUCCESS;
382 error:
383     free(sys);
384     return VLC_EGENERIC;
385 }
386
387 static void Close(vlc_object_t *object)
388 {
389     filter_t     *filter = (filter_t *)object;
390     filter_sys_t *sys    = filter->p_sys;
391
392     free(sys);
393 }