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