cec6ebd2b4209753100db12e0ecb3597ba8cbdff
[vlc.git] / modules / video_filter / transform.c
1 /*****************************************************************************
2  * transform.c : transform image module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2006 VLC authors and VideoLAN
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 it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * 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)
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 HFlip(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 VFlip(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 - 1 - dx;
106     *sy = h - 1 - 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 PLANE(f,bits) \
117 static void Plane##bits##_##f(plane_t *restrict dst, const plane_t *restrict src) \
118 { \
119     const uint##bits##_t *src_pixels = (const void *)src->p_pixels; \
120     uint##bits##_t *restrict dst_pixels = (void *)dst->p_pixels; \
121     const unsigned src_width = src->i_pitch / sizeof (*src_pixels); \
122     const unsigned dst_width = dst->i_pitch / sizeof (*dst_pixels); \
123     const unsigned dst_visible_width = dst->i_visible_pitch / sizeof (*dst_pixels); \
124  \
125     for (int y = 0; y < dst->i_visible_lines; y++) { \
126         for (unsigned x = 0; x < dst_visible_width; x++) { \
127             int sx, sy; \
128             (f)(&sx, &sy, dst_visible_width, dst->i_visible_lines, x, y); \
129             dst_pixels[y * dst_width + x] = \
130                 src_pixels[sy * src_width + sx]; \
131         } \
132     } \
133 }
134
135 static void Plane_VFlip(plane_t *restrict dst, const plane_t *restrict src)
136 {
137     const uint8_t *src_pixels = src->p_pixels;
138     uint8_t *restrict dst_pixels = dst->p_pixels;
139
140     dst_pixels += dst->i_pitch * dst->i_visible_lines;
141     for (int y = 0; y < dst->i_visible_lines; y++) {
142         dst_pixels -= dst->i_pitch;
143         memcpy(dst_pixels, src_pixels, dst->i_visible_pitch);
144         src_pixels += src->i_pitch;
145     }
146 }
147
148 #define I422(f) \
149 static void Plane422_##f(plane_t *restrict dst, const plane_t *restrict src) \
150 { \
151     for (int y = 0; y < dst->i_visible_lines; y += 2) { \
152         for (int x = 0; x < dst->i_visible_pitch; x++) { \
153             int sx, sy, uv; \
154             (f)(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines / 2, \
155                 x, y / 2); \
156             uv = (1 + src->p_pixels[2 * sy * src->i_pitch + sx] + \
157                 src->p_pixels[(2 * sy + 1) * src->i_pitch + sx]) / 2; \
158             dst->p_pixels[y * dst->i_pitch + x] = uv; \
159             dst->p_pixels[(y + 1) * dst->i_pitch + x] = uv; \
160         } \
161     } \
162 }
163
164 #define YUY2(f) \
165 static void PlaneYUY2_##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 #define PLANES(f) \
200 PLANE(f,8) PLANE(f,16) PLANE(f,32)
201
202 PLANES(HFlip)
203 #define Plane8_VFlip Plane_VFlip
204 #define Plane16_VFlip Plane_VFlip
205 #define Plane32_VFlip Plane_VFlip
206 PLANES(Transpose)
207 PLANES(AntiTranspose)
208 PLANES(R90)
209 PLANES(R180)
210 PLANES(R270)
211
212 #define Plane422_HFlip Plane16_HFlip
213 #define Plane422_VFlip Plane_VFlip
214 #define Plane422_R180  Plane16_R180
215 I422(Transpose)
216 I422(AntiTranspose)
217 I422(R90)
218 I422(R270)
219
220 #define PlaneYUY2_HFlip Plane32_HFlip
221 #define PlaneYUY2_VFlip Plane_VFlip
222 #define PlaneYUY2_R180  Plane32_R180
223 YUY2(Transpose)
224 YUY2(AntiTranspose)
225 YUY2(R90)
226 YUY2(R270)
227
228 typedef struct {
229     char      name[16];
230     convert_t convert;
231     convert_t iconvert;
232     video_transform_t operation;
233     void      (*plane8) (plane_t *dst, const plane_t *src);
234     void      (*plane16)(plane_t *dst, const plane_t *src);
235     void      (*plane32)(plane_t *dst, const plane_t *src);
236     void      (*i422)(plane_t *dst, const plane_t *src);
237     void      (*yuyv)(plane_t *dst, const plane_t *src);
238 } transform_description_t;
239
240 #define DESC(str, f, invf, op) \
241     { str, f, invf, op, Plane8_##f, Plane16_##f, Plane32_##f, \
242       Plane422_##f, PlaneYUY2_##f }
243
244 static const transform_description_t descriptions[] = {
245     DESC("90",            R90,           R270,          TRANSFORM_R90),
246     DESC("180",           R180,          R180,          TRANSFORM_R180),
247     DESC("270",           R270,          R90,           TRANSFORM_R270),
248     DESC("hflip",         HFlip,         HFlip,         TRANSFORM_HFLIP),
249     DESC("vflip",         VFlip,         VFlip,         TRANSFORM_VFLIP),
250     DESC("transpose",     Transpose,     Transpose,     TRANSFORM_TRANSPOSE),
251     DESC("antitranspose", AntiTranspose, AntiTranspose, TRANSFORM_ANTI_TRANSPOSE),
252 };
253
254 static bool dsc_is_rotated(const transform_description_t *dsc)
255 {
256     return dsc->plane32 != dsc->yuyv;
257 }
258
259 static const size_t n_transforms =
260     sizeof (descriptions) / sizeof (descriptions[0]);
261
262 struct filter_sys_t {
263     const vlc_chroma_description_t *chroma;
264     void (*plane[PICTURE_PLANE_MAX])(plane_t *, const plane_t *);
265     convert_t convert;
266 };
267
268 static picture_t *Filter(filter_t *filter, picture_t *src)
269 {
270     filter_sys_t *sys = filter->p_sys;
271
272     picture_t *dst = filter_NewPicture(filter);
273     if (!dst) {
274         picture_Release(src);
275         return NULL;
276     }
277
278     const vlc_chroma_description_t *chroma = sys->chroma;
279     for (unsigned i = 0; i < chroma->plane_count; i++)
280          (sys->plane[i])(&dst->p[i], &src->p[i]);
281
282     picture_CopyProperties(dst, src);
283     picture_Release(src);
284     return dst;
285 }
286
287 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
288                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
289 {
290     VLC_UNUSED( mold );
291
292     const video_format_t *fmt = &filter->fmt_out.video;
293     const filter_sys_t   *sys = filter->p_sys;
294
295     *mouse = *mnew;
296     sys->convert(&mouse->i_x, &mouse->i_y,
297                  fmt->i_visible_width, fmt->i_visible_height,
298                  mouse->i_x, mouse->i_y);
299     return VLC_SUCCESS;
300 }
301
302 static int Open(vlc_object_t *object)
303 {
304     filter_t *filter = (filter_t *)object;
305     const video_format_t *src = &filter->fmt_in.video;
306     video_format_t       *dst = &filter->fmt_out.video;
307
308     const vlc_chroma_description_t *chroma =
309         vlc_fourcc_GetChromaDescription(src->i_chroma);
310     if (chroma == NULL)
311         return VLC_EGENERIC;
312
313     filter_sys_t *sys = malloc(sizeof(*sys));
314     if (!sys)
315         return VLC_ENOMEM;
316
317     sys->chroma = chroma;
318
319     static const char *const ppsz_filter_options[] = {
320         "type", NULL
321     };
322
323     config_ChainParse(filter, CFG_PREFIX, ppsz_filter_options,
324                       filter->p_cfg);
325     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
326     const transform_description_t *dsc = NULL;
327
328     for (size_t i = 0; i < n_transforms; i++)
329         if (type_name && !strcmp(descriptions[i].name, type_name)) {
330             dsc = &descriptions[i];
331             break;
332         }
333     if (dsc == NULL) {
334         dsc = &descriptions[0];
335         msg_Warn(filter, "No valid transform mode provided, using '%s'",
336                  dsc->name);
337     }
338
339     free(type_name);
340
341     switch (chroma->pixel_size) {
342         case 1:
343             sys->plane[0] = dsc->plane8;
344             break;
345         case 2:
346             sys->plane[0] = dsc->plane16;
347             break;
348         case 4:
349             sys->plane[0] = dsc->plane32;
350             break;
351         default:
352             msg_Err(filter, "Unsupported pixel size %u (chroma %4.4s)",
353                     chroma->pixel_size, (char *)&src->i_chroma);
354             goto error;
355     }
356
357     for (unsigned i = 1; i < PICTURE_PLANE_MAX; i++)
358         sys->plane[i] = sys->plane[0];
359     sys->convert = dsc->convert;
360
361     if (dsc_is_rotated(dsc)) {
362         switch (src->i_chroma) {
363             case VLC_CODEC_I422:
364             case VLC_CODEC_J422:
365                 sys->plane[2] = sys->plane[1] = dsc->i422;
366                 break;
367             default:
368                 for (unsigned i = 0; i < chroma->plane_count; i++) {
369                     if (chroma->p[i].w.num * chroma->p[i].h.den
370                      != chroma->p[i].h.num * chroma->p[i].w.den) {
371                         msg_Err(filter, "Format rotation not possible "
372                                 "(chroma %4.4s)", (char *)&src->i_chroma);
373                         goto error;
374                     }
375             }
376         }
377     }
378
379     /*
380      * Note: we neither compare nor set dst->orientation,
381      * the caller needs to do it manually (user might want
382      * to transform video without changing the orientation).
383      */
384
385     video_format_t src_trans = *src;
386     video_format_TransformBy(&src_trans, dsc->operation);
387
388     if (!filter->b_allow_fmt_out_change &&
389         (dst->i_width          != src_trans.i_width ||
390          dst->i_visible_width  != src_trans.i_visible_width ||
391          dst->i_height         != src_trans.i_height ||
392          dst->i_visible_height != src_trans.i_visible_height ||
393          dst->i_sar_num        != src_trans.i_sar_num ||
394          dst->i_sar_den        != src_trans.i_sar_den ||
395          dst->i_x_offset       != src_trans.i_x_offset ||
396          dst->i_y_offset       != src_trans.i_y_offset)) {
397
398             msg_Err(filter, "Format change is not allowed");
399             goto error;
400     }
401     else if(filter->b_allow_fmt_out_change) {
402
403         dst->i_width          = src_trans.i_width;
404         dst->i_visible_width  = src_trans.i_visible_width;
405         dst->i_height         = src_trans.i_height;
406         dst->i_visible_height = src_trans.i_visible_height;
407         dst->i_sar_num        = src_trans.i_sar_num;
408         dst->i_sar_den        = src_trans.i_sar_den;
409         dst->i_x_offset       = src_trans.i_x_offset;
410         dst->i_y_offset       = src_trans.i_y_offset;
411     }
412
413     /* Deal with weird packed formats */
414     switch (src->i_chroma) {
415         case VLC_CODEC_UYVY:
416         case VLC_CODEC_VYUY:
417             if (dsc_is_rotated(dsc)) {
418                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
419                         (char *)&src->i_chroma);
420                 goto error;
421             }
422             /* fallthrough */
423         case VLC_CODEC_YUYV:
424         case VLC_CODEC_YVYU:
425             sys->plane[0] = dsc->yuyv; /* 32-bits, not 16-bits! */
426             break;
427         case VLC_CODEC_NV12:
428         case VLC_CODEC_NV21:
429             goto error;
430     }
431
432     filter->p_sys           = sys;
433     filter->pf_video_filter = Filter;
434     filter->pf_video_mouse  = Mouse;
435     return VLC_SUCCESS;
436 error:
437     free(sys);
438     return VLC_EGENERIC;
439 }
440
441 static void Close(vlc_object_t *object)
442 {
443     filter_t     *filter = (filter_t *)object;
444     filter_sys_t *sys    = filter->p_sys;
445
446     free(sys);
447 }