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
8 * Authors: Samuel Hocevar <sam@zoy.org>
9 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
45 #define CFG_PREFIX "transform-"
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") };
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)
63 add_string(CFG_PREFIX "type", "90", TYPE_TEXT, TYPE_TEXT, false)
64 change_string_list(type_list, type_list_text, 0)
67 add_shortcut("transform")
68 set_callbacks(Open, Close)
71 /*****************************************************************************
73 *****************************************************************************/
74 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
80 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
86 static void Transpose(int *sx, int *sy, int w, int h, int dx, int dy)
88 VLC_UNUSED( h ); VLC_UNUSED( w );
92 static void AntiTranspose(int *sx, int *sy, int w, int h, int dx, int dy)
97 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
103 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
108 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
114 typedef void (*convert_t)(int *, int *, int, int, int, int);
116 #define PLANE(f,bits) \
117 static void Plane##bits##_##f(plane_t *restrict dst, const plane_t *restrict src) \
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); \
125 for (int y = 0; y < dst->i_visible_lines; y++) { \
126 for (unsigned x = 0; x < dst_visible_width; x++) { \
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]; \
135 #define PLANE_YUY2(f) \
136 static void PlaneYUY2_##f(plane_t *restrict dst, const plane_t *restrict src) \
138 unsigned dst_visible_width = dst->i_visible_pitch / 2; \
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, \
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]; \
156 (f)(&sx, &sy, dst_visible_width / 2, dst->i_visible_lines / 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; \
171 PLANE(f,8) PLANE(f,16) PLANE(f,32) PLANE_YUY2(f)
176 PLANES(AntiTranspose)
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;
192 #define DESC(str, rotated, f, invf) \
193 { str, rotated, f, invf, Plane8_##f, Plane16_##f, Plane32_##f, PlaneYUY2_##f }
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),
205 static const size_t n_transforms =
206 sizeof (descriptions) / sizeof (descriptions[0]);
208 struct filter_sys_t {
209 const vlc_chroma_description_t *chroma;
210 void (*plane)(plane_t *, const plane_t *);
214 static picture_t *Filter(filter_t *filter, picture_t *src)
216 filter_sys_t *sys = filter->p_sys;
218 picture_t *dst = filter_NewPicture(filter);
220 picture_Release(src);
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]);
228 picture_CopyProperties(dst, src);
229 picture_Release(src);
233 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
234 const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
238 const video_format_t *fmt = &filter->fmt_out.video;
239 const filter_sys_t *sys = filter->p_sys;
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);
248 static int Open(vlc_object_t *object)
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;
254 const vlc_chroma_description_t *chroma =
255 vlc_fourcc_GetChromaDescription(src->i_chroma);
259 filter_sys_t *sys = malloc(sizeof(*sys));
263 sys->chroma = chroma;
265 char *type_name = var_InheritString(filter, CFG_PREFIX"type");
266 const transform_description_t *dsc = NULL;
268 for (size_t i = 0; i < n_transforms; i++)
269 if (type_name && !strcmp(descriptions[i].name, type_name)) {
270 dsc = &descriptions[i];
274 dsc = &descriptions[0];
275 msg_Warn(filter, "No valid transform mode provided, using '%s'",
281 switch (chroma->pixel_size) {
283 sys->plane = dsc->plane8;
286 sys->plane = dsc->plane16;
289 sys->plane = dsc->plane32;
292 msg_Err(filter, "Unsupported pixel size %u (chroma %4.4s)",
293 chroma->pixel_size, (char *)&src->i_chroma);
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... */
308 if (!filter->b_allow_fmt_out_change) {
309 msg_Err(filter, "Format change is not allowed");
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;
321 /* Deal with weird packed formats */
322 switch (src->i_chroma) {
325 sys->plane = dsc->is_rotated ? dsc->yuyv : dsc->plane32;
329 if (dsc->is_rotated) {
330 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
331 (char *)&src->i_chroma);
334 sys->plane = dsc->plane32; /* 32-bits, not 16-bits! */
341 dst->i_x_offset = INT_MAX;
342 dst->i_y_offset = INT_MAX;
343 for (int i = 0; i < 2; i++) {
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));
353 filter->pf_video_filter = Filter;
354 filter->pf_video_mouse = Mouse;
361 static void Close(vlc_object_t *object)
363 filter_t *filter = (filter_t *)object;
364 filter_sys_t *sys = filter->p_sys;