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>
38 #include <vlc_mouse.h>
39 #include <vlc_picture.h>
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Open (vlc_object_t
*);
45 static void Close(vlc_object_t
*);
47 #define CFG_PREFIX "transform-"
49 #define TYPE_TEXT N_("Transform type")
50 static const char * const type_list
[] = { "90", "180", "270",
51 "hflip", "vflip", "transpose", "antitranspose" };
52 static const char * const type_list_text
[] = { N_("Rotate by 90 degrees"),
53 N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
54 N_("Flip horizontally"), N_("Flip vertically"),
55 N_("Transpose"), N_("Anti-transpose") };
58 set_description(N_("Video transformation filter"))
59 set_shortname(N_("Transformation"))
60 set_help(N_("Rotate or flip the video"))
61 set_capability("video filter", 0)
62 set_category(CAT_VIDEO
)
63 set_subcategory(SUBCAT_VIDEO_VFILTER
)
65 add_string(CFG_PREFIX
"type", "90", TYPE_TEXT
, TYPE_TEXT
, false)
66 change_string_list(type_list
, type_list_text
)
69 add_shortcut("transform")
70 set_callbacks(Open
, Close
)
73 /*****************************************************************************
75 *****************************************************************************/
76 static void HFlip(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
82 static void VFlip(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
88 static void Transpose(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
90 VLC_UNUSED( h
); VLC_UNUSED( w
);
94 static void AntiTranspose(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
99 static void R90(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
105 static void R180(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
110 static void R270(int *sx
, int *sy
, int w
, int h
, int dx
, int dy
)
116 typedef void (*convert_t
)(int *, int *, int, int, int, int);
118 #define PLANE(f,bits) \
119 static void Plane##bits##_##f(plane_t *restrict dst, const plane_t *restrict src) \
121 const uint##bits##_t *src_pixels = (const void *)src->p_pixels; \
122 uint##bits##_t *restrict dst_pixels = (void *)dst->p_pixels; \
123 const unsigned src_width = src->i_pitch / sizeof (*src_pixels); \
124 const unsigned dst_width = dst->i_pitch / sizeof (*dst_pixels); \
125 const unsigned dst_visible_width = dst->i_visible_pitch / sizeof (*dst_pixels); \
127 for (int y = 0; y < dst->i_visible_lines; y++) { \
128 for (unsigned x = 0; x < dst_visible_width; x++) { \
130 (f)(&sx, &sy, dst_visible_width, dst->i_visible_lines, x, y); \
131 dst_pixels[y * dst_width + x] = \
132 src_pixels[sy * src_width + sx]; \
137 static void Plane_VFlip(plane_t
*restrict dst
, const plane_t
*restrict src
)
139 const uint8_t *src_pixels
= src
->p_pixels
;
140 uint8_t *restrict dst_pixels
= dst
->p_pixels
;
142 dst_pixels
+= dst
->i_pitch
* dst
->i_visible_lines
;
143 for (int y
= 0; y
< dst
->i_visible_lines
; y
++) {
144 dst_pixels
-= dst
->i_pitch
;
145 memcpy(dst_pixels
, src_pixels
, dst
->i_visible_pitch
);
146 src_pixels
+= src
->i_pitch
;
151 static void Plane422_##f(plane_t *restrict dst, const plane_t *restrict src) \
153 for (int y = 0; y < dst->i_visible_lines; y += 2) { \
154 for (int x = 0; x < dst->i_visible_pitch; x++) { \
156 (f)(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines / 2, \
158 uv = (1 + src->p_pixels[2 * sy * src->i_pitch + sx] + \
159 src->p_pixels[(2 * sy + 1) * src->i_pitch + sx]) / 2; \
160 dst->p_pixels[y * dst->i_pitch + x] = uv; \
161 dst->p_pixels[(y + 1) * dst->i_pitch + x] = uv; \
167 static void PlaneYUY2_##f(plane_t *restrict dst, const plane_t *restrict src) \
169 unsigned dst_visible_width = dst->i_visible_pitch / 2; \
171 for (int y = 0; y < dst->i_visible_lines; y += 2) { \
172 for (unsigned x = 0; x < dst_visible_width; x+= 2) { \
173 int sx0, sy0, sx1, sy1; \
174 (f)(&sx0, &sy0, dst_visible_width, dst->i_visible_lines, x, y); \
175 (f)(&sx1, &sy1, dst_visible_width, dst->i_visible_lines, \
177 dst->p_pixels[(y + 0) * dst->i_pitch + 2 * (x + 0)] = \
178 src->p_pixels[sy0 * src->i_pitch + 2 * sx0]; \
179 dst->p_pixels[(y + 0) * dst->i_pitch + 2 * (x + 1)] = \
180 src->p_pixels[sy1 * src->i_pitch + 2 * sx0]; \
181 dst->p_pixels[(y + 1) * dst->i_pitch + 2 * (x + 0)] = \
182 src->p_pixels[sy0 * src->i_pitch + 2 * sx1]; \
183 dst->p_pixels[(y + 1) * dst->i_pitch + 2 * (x + 1)] = \
184 src->p_pixels[sy1 * src->i_pitch + 2 * sx1]; \
187 (f)(&sx, &sy, dst_visible_width / 2, dst->i_visible_lines / 2, \
189 u = (1 + src->p_pixels[2 * sy * src->i_pitch + 4 * sx + 1] + \
190 src->p_pixels[(2 * sy + 1) * src->i_pitch + 4 * sx + 1]) / 2; \
191 v = (1 + src->p_pixels[2 * sy * src->i_pitch + 4 * sx + 3] + \
192 src->p_pixels[(2 * sy + 1) * src->i_pitch + 4 * sx + 3]) / 2; \
193 dst->p_pixels[(y + 0) * dst->i_pitch + 2 * x + 1] = u; \
194 dst->p_pixels[(y + 0) * dst->i_pitch + 2 * x + 3] = v; \
195 dst->p_pixels[(y + 1) * dst->i_pitch + 2 * x + 1] = u; \
196 dst->p_pixels[(y + 1) * dst->i_pitch + 2 * x + 3] = v; \
202 PLANE(f,8) PLANE(f,16) PLANE(f,32)
205 #define Plane8_VFlip Plane_VFlip
206 #define Plane16_VFlip Plane_VFlip
207 #define Plane32_VFlip Plane_VFlip
209 PLANES(AntiTranspose
)
214 #define Plane422_HFlip Plane16_HFlip
215 #define Plane422_VFlip Plane_VFlip
216 #define Plane422_R180 Plane16_R180
222 #define PlaneYUY2_HFlip Plane32_HFlip
223 #define PlaneYUY2_VFlip Plane_VFlip
224 #define PlaneYUY2_R180 Plane32_R180
234 video_transform_t operation
;
235 void (*plane8
) (plane_t
*dst
, const plane_t
*src
);
236 void (*plane16
)(plane_t
*dst
, const plane_t
*src
);
237 void (*plane32
)(plane_t
*dst
, const plane_t
*src
);
238 void (*i422
)(plane_t
*dst
, const plane_t
*src
);
239 void (*yuyv
)(plane_t
*dst
, const plane_t
*src
);
240 } transform_description_t
;
242 #define DESC(str, f, invf, op) \
243 { str, f, invf, op, Plane8_##f, Plane16_##f, Plane32_##f, \
244 Plane422_##f, PlaneYUY2_##f }
246 static const transform_description_t descriptions
[] = {
247 DESC("90", R90
, R270
, TRANSFORM_R90
),
248 DESC("180", R180
, R180
, TRANSFORM_R180
),
249 DESC("270", R270
, R90
, TRANSFORM_R270
),
250 DESC("hflip", HFlip
, HFlip
, TRANSFORM_HFLIP
),
251 DESC("vflip", VFlip
, VFlip
, TRANSFORM_VFLIP
),
252 DESC("transpose", Transpose
, Transpose
, TRANSFORM_TRANSPOSE
),
253 DESC("antitranspose", AntiTranspose
, AntiTranspose
, TRANSFORM_ANTI_TRANSPOSE
),
256 static bool dsc_is_rotated(const transform_description_t
*dsc
)
258 return dsc
->plane32
!= dsc
->yuyv
;
261 static const size_t n_transforms
=
262 sizeof (descriptions
) / sizeof (descriptions
[0]);
264 struct filter_sys_t
{
265 const vlc_chroma_description_t
*chroma
;
266 void (*plane
[PICTURE_PLANE_MAX
])(plane_t
*, const plane_t
*);
270 static picture_t
*Filter(filter_t
*filter
, picture_t
*src
)
272 filter_sys_t
*sys
= filter
->p_sys
;
274 picture_t
*dst
= filter_NewPicture(filter
);
276 picture_Release(src
);
280 const vlc_chroma_description_t
*chroma
= sys
->chroma
;
281 for (unsigned i
= 0; i
< chroma
->plane_count
; i
++)
282 (sys
->plane
[i
])(&dst
->p
[i
], &src
->p
[i
]);
284 picture_CopyProperties(dst
, src
);
285 picture_Release(src
);
289 static int Mouse(filter_t
*filter
, vlc_mouse_t
*mouse
,
290 const vlc_mouse_t
*mold
, const vlc_mouse_t
*mnew
)
294 const video_format_t
*fmt
= &filter
->fmt_out
.video
;
295 const filter_sys_t
*sys
= filter
->p_sys
;
298 sys
->convert(&mouse
->i_x
, &mouse
->i_y
,
299 fmt
->i_visible_width
, fmt
->i_visible_height
,
300 mouse
->i_x
, mouse
->i_y
);
304 static int Open(vlc_object_t
*object
)
306 filter_t
*filter
= (filter_t
*)object
;
307 const video_format_t
*src
= &filter
->fmt_in
.video
;
308 video_format_t
*dst
= &filter
->fmt_out
.video
;
310 const vlc_chroma_description_t
*chroma
=
311 vlc_fourcc_GetChromaDescription(src
->i_chroma
);
315 filter_sys_t
*sys
= malloc(sizeof(*sys
));
319 sys
->chroma
= chroma
;
321 static const char *const ppsz_filter_options
[] = {
325 config_ChainParse(filter
, CFG_PREFIX
, ppsz_filter_options
,
327 char *type_name
= var_InheritString(filter
, CFG_PREFIX
"type");
328 const transform_description_t
*dsc
= NULL
;
330 for (size_t i
= 0; i
< n_transforms
; i
++)
331 if (type_name
&& !strcmp(descriptions
[i
].name
, type_name
)) {
332 dsc
= &descriptions
[i
];
336 dsc
= &descriptions
[0];
337 msg_Warn(filter
, "No valid transform mode provided, using '%s'",
343 switch (chroma
->pixel_size
) {
345 sys
->plane
[0] = dsc
->plane8
;
348 sys
->plane
[0] = dsc
->plane16
;
351 sys
->plane
[0] = dsc
->plane32
;
354 msg_Err(filter
, "Unsupported pixel size %u (chroma %4.4s)",
355 chroma
->pixel_size
, (char *)&src
->i_chroma
);
359 for (unsigned i
= 1; i
< PICTURE_PLANE_MAX
; i
++)
360 sys
->plane
[i
] = sys
->plane
[0];
361 sys
->convert
= dsc
->convert
;
363 if (dsc_is_rotated(dsc
)) {
364 switch (src
->i_chroma
) {
367 sys
->plane
[2] = sys
->plane
[1] = dsc
->i422
;
370 for (unsigned i
= 0; i
< chroma
->plane_count
; i
++) {
371 if (chroma
->p
[i
].w
.num
* chroma
->p
[i
].h
.den
372 != chroma
->p
[i
].h
.num
* chroma
->p
[i
].w
.den
) {
373 msg_Err(filter
, "Format rotation not possible "
374 "(chroma %4.4s)", (char *)&src
->i_chroma
);
382 * Note: we neither compare nor set dst->orientation,
383 * the caller needs to do it manually (user might want
384 * to transform video without changing the orientation).
387 video_format_t src_trans
= *src
;
388 video_format_TransformBy(&src_trans
, dsc
->operation
);
390 if (!filter
->b_allow_fmt_out_change
&&
391 (dst
->i_width
!= src_trans
.i_width
||
392 dst
->i_visible_width
!= src_trans
.i_visible_width
||
393 dst
->i_height
!= src_trans
.i_height
||
394 dst
->i_visible_height
!= src_trans
.i_visible_height
||
395 dst
->i_sar_num
!= src_trans
.i_sar_num
||
396 dst
->i_sar_den
!= src_trans
.i_sar_den
||
397 dst
->i_x_offset
!= src_trans
.i_x_offset
||
398 dst
->i_y_offset
!= src_trans
.i_y_offset
)) {
400 msg_Err(filter
, "Format change is not allowed");
403 else if(filter
->b_allow_fmt_out_change
) {
405 dst
->i_width
= src_trans
.i_width
;
406 dst
->i_visible_width
= src_trans
.i_visible_width
;
407 dst
->i_height
= src_trans
.i_height
;
408 dst
->i_visible_height
= src_trans
.i_visible_height
;
409 dst
->i_sar_num
= src_trans
.i_sar_num
;
410 dst
->i_sar_den
= src_trans
.i_sar_den
;
411 dst
->i_x_offset
= src_trans
.i_x_offset
;
412 dst
->i_y_offset
= src_trans
.i_y_offset
;
415 /* Deal with weird packed formats */
416 switch (src
->i_chroma
) {
419 if (dsc_is_rotated(dsc
)) {
420 msg_Err(filter
, "Format rotation not possible (chroma %4.4s)",
421 (char *)&src
->i_chroma
);
427 sys
->plane
[0] = dsc
->yuyv
; /* 32-bits, not 16-bits! */
435 filter
->pf_video_filter
= Filter
;
436 filter
->pf_video_mouse
= Mouse
;
443 static void Close(vlc_object_t
*object
)
445 filter_t
*filter
= (filter_t
*)object
;
446 filter_sys_t
*sys
= filter
->p_sys
;