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
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 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.
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.
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 *****************************************************************************/
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)
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 static void Plane_VFlip(plane_t *restrict dst, const plane_t *restrict src)
137 const uint8_t *src_pixels = src->p_pixels;
138 uint8_t *restrict dst_pixels = dst->p_pixels;
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;
149 static void Plane422_##f(plane_t *restrict dst, const plane_t *restrict src) \
151 for (int y = 0; y < dst->i_visible_lines; y += 2) { \
152 for (int x = 0; x < dst->i_visible_pitch; x++) { \
154 (f)(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines / 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; \
165 static void PlaneYUY2_##f(plane_t *restrict dst, const plane_t *restrict src) \
167 unsigned dst_visible_width = dst->i_visible_pitch / 2; \
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, \
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]; \
185 (f)(&sx, &sy, dst_visible_width / 2, dst->i_visible_lines / 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; \
200 PLANE(f,8) PLANE(f,16) PLANE(f,32)
203 #define Plane8_VFlip Plane_VFlip
204 #define Plane16_VFlip Plane_VFlip
205 #define Plane32_VFlip Plane_VFlip
207 PLANES(AntiTranspose)
212 #define Plane422_HFlip Plane16_HFlip
213 #define Plane422_VFlip Plane_VFlip
214 #define Plane422_R180 Plane16_R180
220 #define PlaneYUY2_HFlip Plane32_HFlip
221 #define PlaneYUY2_VFlip Plane_VFlip
222 #define PlaneYUY2_R180 Plane32_R180
232 void (*plane8) (plane_t *dst, const plane_t *src);
233 void (*plane16)(plane_t *dst, const plane_t *src);
234 void (*plane32)(plane_t *dst, const plane_t *src);
235 void (*i422)(plane_t *dst, const plane_t *src);
236 void (*yuyv)(plane_t *dst, const plane_t *src);
237 } transform_description_t;
239 #define DESC(str, f, invf) \
240 { str, f, invf, Plane8_##f, Plane16_##f, Plane32_##f, \
241 Plane422_##f, PlaneYUY2_##f }
243 static const transform_description_t descriptions[] = {
244 DESC("90", R90, R270),
245 DESC("180", R180, R180),
246 DESC("270", R270, R90),
247 DESC("hflip", HFlip, HFlip),
248 DESC("vflip", VFlip, VFlip),
249 DESC("transpose", Transpose, Transpose),
250 DESC("antitranspose", AntiTranspose, AntiTranspose),
253 static bool dsc_is_rotated(const transform_description_t *dsc)
255 return dsc->plane32 != dsc->yuyv;
258 static const size_t n_transforms =
259 sizeof (descriptions) / sizeof (descriptions[0]);
261 struct filter_sys_t {
262 const vlc_chroma_description_t *chroma;
263 void (*plane[PICTURE_PLANE_MAX])(plane_t *, const plane_t *);
267 static picture_t *Filter(filter_t *filter, picture_t *src)
269 filter_sys_t *sys = filter->p_sys;
271 picture_t *dst = filter_NewPicture(filter);
273 picture_Release(src);
277 const vlc_chroma_description_t *chroma = sys->chroma;
278 for (unsigned i = 0; i < chroma->plane_count; i++)
279 (sys->plane[i])(&dst->p[i], &src->p[i]);
281 picture_CopyProperties(dst, src);
282 picture_Release(src);
286 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
287 const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
291 const video_format_t *fmt = &filter->fmt_out.video;
292 const filter_sys_t *sys = filter->p_sys;
295 sys->convert(&mouse->i_x, &mouse->i_y,
296 fmt->i_visible_width, fmt->i_visible_height,
297 mouse->i_x, mouse->i_y);
301 static int Open(vlc_object_t *object)
303 filter_t *filter = (filter_t *)object;
304 const video_format_t *src = &filter->fmt_in.video;
305 video_format_t *dst = &filter->fmt_out.video;
307 const vlc_chroma_description_t *chroma =
308 vlc_fourcc_GetChromaDescription(src->i_chroma);
312 filter_sys_t *sys = malloc(sizeof(*sys));
316 sys->chroma = chroma;
318 static const char *const ppsz_filter_options[] = {
322 config_ChainParse(filter, CFG_PREFIX, ppsz_filter_options,
324 char *type_name = var_InheritString(filter, CFG_PREFIX"type");
325 const transform_description_t *dsc = NULL;
327 for (size_t i = 0; i < n_transforms; i++)
328 if (type_name && !strcmp(descriptions[i].name, type_name)) {
329 dsc = &descriptions[i];
333 dsc = &descriptions[0];
334 msg_Warn(filter, "No valid transform mode provided, using '%s'",
340 switch (chroma->pixel_size) {
342 sys->plane[0] = dsc->plane8;
345 sys->plane[0] = dsc->plane16;
348 sys->plane[0] = dsc->plane32;
351 msg_Err(filter, "Unsupported pixel size %u (chroma %4.4s)",
352 chroma->pixel_size, (char *)&src->i_chroma);
356 for (unsigned i = 1; i < PICTURE_PLANE_MAX; i++)
357 sys->plane[i] = sys->plane[0];
358 sys->convert = dsc->convert;
360 if (dsc_is_rotated(dsc)) {
361 switch (src->i_chroma) {
364 sys->plane[2] = sys->plane[1] = dsc->i422;
367 for (unsigned i = 0; i < chroma->plane_count; i++) {
368 if (chroma->p[i].w.num * chroma->p[i].h.den
369 != chroma->p[i].h.num * chroma->p[i].w.den) {
370 msg_Err(filter, "Format rotation not possible "
371 "(chroma %4.4s)", (char *)&src->i_chroma);
377 if (!filter->b_allow_fmt_out_change) {
378 msg_Err(filter, "Format change is not allowed");
382 dst->i_width = src->i_height;
383 dst->i_visible_width = src->i_visible_height;
384 dst->i_height = src->i_width;
385 dst->i_visible_height = src->i_visible_width;
386 dst->i_sar_num = src->i_sar_den;
387 dst->i_sar_den = src->i_sar_num;
390 /* Deal with weird packed formats */
391 switch (src->i_chroma) {
394 if (dsc_is_rotated(dsc)) {
395 msg_Err(filter, "Format rotation not possible (chroma %4.4s)",
396 (char *)&src->i_chroma);
402 sys->plane[0] = dsc->yuyv; /* 32-bits, not 16-bits! */
409 dst->i_x_offset = INT_MAX;
410 dst->i_y_offset = INT_MAX;
411 for (int i = 0; i < 2; i++) {
413 dsc->iconvert(&tx, &ty, src->i_width, src->i_height,
414 src->i_x_offset + i * (src->i_visible_width - 1),
415 src->i_y_offset + i * (src->i_visible_height - 1));
416 dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
417 dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
421 filter->pf_video_filter = Filter;
422 filter->pf_video_mouse = Mouse;
429 static void Close(vlc_object_t *object)
431 filter_t *filter = (filter_t *)object;
432 filter_sys_t *sys = filter->p_sys;