]> git.sesse.net Git - vlc/blob - modules/video_filter/transform.c
transform: factor common macro code
[vlc] / 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 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 - 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 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 #define PLANE_YUY2(f) \
136 static void PlaneYUY2_##f(plane_t *restrict dst, const plane_t *restrict src) \
137 { \
138     unsigned dst_visible_width = dst->i_visible_pitch / 2; \
139  \
140     for (int y = 0; y < dst->i_visible_lines; y += 2) { \
141         for (unsigned x = 0; x < dst_visible_width; x+= 2) { \
142             int sx0, sy0, sx1, sy1; \
143             (f)(&sx0, &sy0, dst_visible_width, dst->i_visible_lines, x, y); \
144             (f)(&sx1, &sy1, dst_visible_width, dst->i_visible_lines, \
145                 x + 1, y + 1); \
146             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * (x + 0)] = \
147                 src->p_pixels[sy0 * src->i_pitch + 2 * sx0]; \
148             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * (x + 1)] = \
149                 src->p_pixels[sy1 * src->i_pitch + 2 * sx0]; \
150             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * (x + 0)] = \
151                 src->p_pixels[sy0 * src->i_pitch + 2 * sx1]; \
152             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * (x + 1)] = \
153                 src->p_pixels[sy1 * src->i_pitch + 2 * sx1]; \
154  \
155             int sx, sy, u, v; \
156             (f)(&sx, &sy, dst_visible_width / 2, dst->i_visible_lines / 2, \
157                 x / 2, y / 2); \
158             u = (1 + src->p_pixels[2 * sy * src->i_pitch + 4 * sx + 1] + \
159                 src->p_pixels[(2 * sy + 1) * src->i_pitch + 4 * sx + 1]) / 2; \
160             v = (1 + src->p_pixels[2 * sy * src->i_pitch + 4 * sx + 3] + \
161                 src->p_pixels[(2 * sy + 1) * src->i_pitch + 4 * sx + 3]) / 2; \
162             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * x + 1] = u; \
163             dst->p_pixels[(y + 0) * dst->i_pitch + 2 * x + 3] = v; \
164             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * x + 1] = u; \
165             dst->p_pixels[(y + 1) * dst->i_pitch + 2 * x + 3] = v; \
166         } \
167     } \
168 }
169
170 #define PLANES(f) \
171 PLANE(f,8) PLANE(f,16) PLANE(f,32) PLANE_YUY2(f)
172
173 PLANES(HFlip)
174 PLANES(VFlip)
175 PLANES(Transpose)
176 PLANES(AntiTranspose)
177 PLANES(R90)
178 PLANES(R180)
179 PLANES(R270)
180
181 typedef struct {
182     char      name[16];
183     bool      is_rotated;
184     convert_t convert;
185     convert_t iconvert;
186     void      (*plane8) (plane_t *dst, const plane_t *src);
187     void      (*plane16)(plane_t *dst, const plane_t *src);
188     void      (*plane32)(plane_t *dst, const plane_t *src);
189     void      (*yuyv)(plane_t *dst, const plane_t *src);
190 } transform_description_t;
191
192 #define DESC(str, rotated, f, invf) \
193     { str, rotated, f, invf, Plane8_##f, Plane16_##f, Plane32_##f, PlaneYUY2_##f }
194
195 static const transform_description_t descriptions[] = {
196     DESC("90",            true,  R90,           R270),
197     DESC("180",           false, R180,          R180),
198     DESC("270",           true,  R270,          R90),
199     DESC("hflip",         false, HFlip,         HFlip),
200     DESC("vflip",         false, VFlip,         VFlip),
201     DESC("transpose",     true,  Transpose,     Transpose),
202     DESC("antitranspose", true,  AntiTranspose, AntiTranspose),
203 };
204
205 static const size_t n_transforms =
206     sizeof (descriptions) / sizeof (descriptions[0]);
207
208 struct filter_sys_t {
209     const vlc_chroma_description_t *chroma;
210     void (*plane)(plane_t *, const plane_t *);
211     convert_t convert;
212 };
213
214 static picture_t *Filter(filter_t *filter, picture_t *src)
215 {
216     filter_sys_t *sys = filter->p_sys;
217
218     picture_t *dst = filter_NewPicture(filter);
219     if (!dst) {
220         picture_Release(src);
221         return NULL;
222     }
223
224     const vlc_chroma_description_t *chroma = sys->chroma;
225     for (unsigned i = 0; i < chroma->plane_count; i++)
226          sys->plane(&dst->p[i], &src->p[i]);
227
228     picture_CopyProperties(dst, src);
229     picture_Release(src);
230     return dst;
231 }
232
233 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
234                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
235 {
236     VLC_UNUSED( mold );
237
238     const video_format_t *fmt = &filter->fmt_out.video;
239     const filter_sys_t   *sys = filter->p_sys;
240
241     *mouse = *mnew;
242     sys->convert(&mouse->i_x, &mouse->i_y,
243                  fmt->i_visible_width, fmt->i_visible_height,
244                  mouse->i_x, mouse->i_y);
245     return VLC_SUCCESS;
246 }
247
248 static int Open(vlc_object_t *object)
249 {
250     filter_t *filter = (filter_t *)object;
251     const video_format_t *src = &filter->fmt_in.video;
252     video_format_t       *dst = &filter->fmt_out.video;
253
254     const vlc_chroma_description_t *chroma =
255         vlc_fourcc_GetChromaDescription(src->i_chroma);
256     if (chroma == NULL)
257         return VLC_EGENERIC;
258
259     filter_sys_t *sys = malloc(sizeof(*sys));
260     if (!sys)
261         return VLC_ENOMEM;
262
263     sys->chroma = chroma;
264
265     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
266     const transform_description_t *dsc = NULL;
267
268     for (size_t i = 0; i < n_transforms; i++)
269         if (type_name && !strcmp(descriptions[i].name, type_name)) {
270             dsc = &descriptions[i];
271             break;
272         }
273     if (dsc == NULL) {
274         dsc = &descriptions[0];
275         msg_Warn(filter, "No valid transform mode provided, using '%s'",
276                  dsc->name);
277     }
278
279     free(type_name);
280
281     switch (chroma->pixel_size) {
282         case 1:
283             sys->plane = dsc->plane8;
284             break;
285         case 2:
286             sys->plane = dsc->plane16;
287             break;
288         case 4:
289             sys->plane = dsc->plane32;
290             break;
291         default:
292             msg_Err(filter, "Unsupported pixel size %u (chroma %4.4s)",
293                     chroma->pixel_size, (char *)&src->i_chroma);
294             goto error;
295     }
296
297     sys->convert = dsc->convert;
298     if (dsc->is_rotated) {
299         for (unsigned i = 0; i < chroma->plane_count; i++) {
300             if (chroma->p[i].w.num * chroma->p[i].h.den
301              != chroma->p[i].h.num * chroma->p[i].w.den) {
302                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
303                         (char *)&src->i_chroma); /* I422... */
304                 goto error;
305             }
306         }
307
308         if (!filter->b_allow_fmt_out_change) {
309             msg_Err(filter, "Format change is not allowed");
310             goto error;
311         }
312
313         dst->i_width          = src->i_height;
314         dst->i_visible_width  = src->i_visible_height;
315         dst->i_height         = src->i_width;
316         dst->i_visible_height = src->i_visible_width;
317         dst->i_sar_num        = src->i_sar_den;
318         dst->i_sar_den        = src->i_sar_num;
319     }
320
321     /* Deal with weird packed formats */
322     switch (src->i_chroma) {
323         case VLC_CODEC_YUYV:
324         case VLC_CODEC_YVYU:
325             sys->plane = dsc->is_rotated ? dsc->yuyv : dsc->plane32;
326             break;
327         case VLC_CODEC_UYVY:
328         case VLC_CODEC_VYUY:
329             if (dsc->is_rotated) {
330                 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
331                         (char *)&src->i_chroma);
332                 goto error;
333             }
334             sys->plane = dsc->plane32; /* 32-bits, not 16-bits! */
335             break;
336         case VLC_CODEC_NV12:
337         case VLC_CODEC_NV21:
338             goto error;
339     }
340
341     dst->i_x_offset       = INT_MAX;
342     dst->i_y_offset       = INT_MAX;
343     for (int i = 0; i < 2; i++) {
344         int tx, ty;
345         dsc->iconvert(&tx, &ty, src->i_width, src->i_height,
346                       src->i_x_offset + i * (src->i_visible_width  - 1),
347                       src->i_y_offset + i * (src->i_visible_height - 1));
348         dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
349         dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
350     }
351
352     filter->p_sys           = sys;
353     filter->pf_video_filter = Filter;
354     filter->pf_video_mouse  = Mouse;
355     return VLC_SUCCESS;
356 error:
357     free(sys);
358     return VLC_EGENERIC;
359 }
360
361 static void Close(vlc_object_t *object)
362 {
363     filter_t     *filter = (filter_t *)object;
364     filter_sys_t *sys    = filter->p_sys;
365
366     free(sys);
367 }