1 /*****************************************************************************
2 * transform.c : transform image module for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2006 the VideoLAN team
5 * Copyright (C) 2010 Laurent Aimar
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 #define TYPE_LONGTEXT N_("One of '90', '180', '270', 'hflip' and 'vflip'")
49 static const char * const type_list[] = { "90", "180", "270", "hflip", "vflip" };
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") };
55 set_description(N_("Video transformation filter"))
56 set_shortname(N_("Transformation"))
57 set_help(N_("Rotate or flip the video"))
58 set_capability("video filter2", 0)
59 set_category(CAT_VIDEO)
60 set_subcategory(SUBCAT_VIDEO_VFILTER)
62 add_string(CFG_PREFIX "type", "90", TYPE_TEXT, TYPE_LONGTEXT, false)
63 change_string_list(type_list, type_list_text, 0)
65 add_shortcut("transform")
66 set_callbacks(Open, Close)
69 /*****************************************************************************
71 *****************************************************************************/
72 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
78 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
84 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
90 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
95 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
101 typedef void (*convert_t)(int *, int *, int, int, int, int);
103 static void Planar(plane_t *dst, const plane_t *src, convert_t f)
105 for (int y = 0; y < dst->i_visible_lines; y++) {
106 for (int x = 0; x < dst->i_visible_pitch; x++) {
108 f(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines, x, y);
109 dst->p_pixels[y * dst->i_pitch + x] = src->p_pixels[sy * src->i_pitch + sx];
115 static void Planar##f(plane_t *dst, const plane_t *src) { Planar(dst, src, f); }
128 void (*planar)(plane_t *dst, const plane_t *src);
129 } transform_description_t;
131 static const transform_description_t descriptions[] = {
132 { "90", true, R90, R270, PlanarR90, },
133 { "180", false, R180, R180, PlanarR180, },
134 { "270", true, R270, R90, PlanarR270, },
135 { "hflip", false, HFlip, HFlip, PlanarHFlip, },
136 { "vflip", false, VFlip, VFlip, PlanarVFlip, },
138 { "", false, NULL, NULL, NULL, }
141 struct filter_sys_t {
142 const transform_description_t *dsc;
143 const vlc_chroma_description_t *chroma;
146 static picture_t *Filter(filter_t *filter, picture_t *src)
148 filter_sys_t *sys = filter->p_sys;
150 picture_t *dst = filter_NewPicture(filter);
152 picture_Release(src);
156 const vlc_chroma_description_t *chroma = sys->chroma;
157 if (chroma->plane_count < 3) {
160 for (unsigned i = 0; i < chroma->plane_count; i++)
161 sys->dsc->planar(&dst->p[i], &src->p[i]);
164 picture_CopyProperties(dst, src);
165 picture_Release(src);
169 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
170 const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
174 const video_format_t *fmt = &filter->fmt_out.video;
175 const transform_description_t *dsc = filter->p_sys->dsc;
178 dsc->convert(&mouse->i_x, &mouse->i_y,
179 fmt->i_visible_width, fmt->i_visible_height, mouse->i_x, mouse->i_y);
183 static int Open(vlc_object_t *object)
185 filter_t *filter = (filter_t *)object;
186 const video_format_t *src = &filter->fmt_in.video;
187 video_format_t *dst = &filter->fmt_out.video;
189 const vlc_chroma_description_t *chroma =
190 vlc_fourcc_GetChromaDescription(src->i_chroma);
191 if (!chroma || chroma->plane_count < 3) {
192 msg_Err(filter, "Unsupported chroma (%4.4s)", (char*)&src->i_chroma);
193 /* TODO support packed and rgb */
197 filter_sys_t *sys = malloc(sizeof(*sys));
201 sys->chroma = chroma;
203 char *type_name = var_InheritString(filter, CFG_PREFIX"type");
206 for (int i = 0; !sys->dsc && *descriptions[i].name; i++) {
207 if (type_name && *type_name && !strcmp(descriptions[i].name, type_name))
208 sys->dsc = &descriptions[i];
211 sys->dsc = &descriptions[0];
212 msg_Warn(filter, "No valid transform mode provided, using '%s'", sys->dsc->name);
217 if (sys->dsc->is_rotated) {
218 if (!filter->b_allow_fmt_out_change) {
219 msg_Err(filter, "Format change is not allowed");
224 dst->i_width = src->i_height;
225 dst->i_visible_width = src->i_visible_height;
226 dst->i_height = src->i_width;
227 dst->i_visible_height = src->i_visible_width;
228 dst->i_sar_num = src->i_sar_den;
229 dst->i_sar_den = src->i_sar_num;
232 dst->i_x_offset = INT_MAX;
233 dst->i_y_offset = INT_MAX;
234 for (int i = 0; i < 2; i++) {
236 sys->dsc->iconvert(&tx, &ty,
237 src->i_width, src->i_height,
238 src->i_x_offset + i * (src->i_visible_width - 1),
239 src->i_y_offset + i * (src->i_visible_height - 1));
240 dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
241 dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
245 filter->pf_video_filter = Filter;
246 filter->pf_video_mouse = Mouse;
250 static void Close(vlc_object_t *object)
252 filter_t *filter = (filter_t *)object;
253 filter_sys_t *sys = filter->p_sys;
259 static void FilterI422( vout_thread_t *p_vout,
260 const picture_t *p_pic, picture_t *p_outpic )
263 switch( p_vout->p_sys->i_mode )
265 case TRANSFORM_MODE_180:
266 case TRANSFORM_MODE_HFLIP:
267 case TRANSFORM_MODE_VFLIP:
268 /* Fall back on the default implementation */
269 FilterPlanar( p_vout, p_pic, p_outpic );
272 case TRANSFORM_MODE_90:
273 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
275 int i_pitch = p_pic->p[i_index].i_pitch;
277 uint8_t *p_in = p_pic->p[i_index].p_pixels;
279 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
280 uint8_t *p_out_end = p_out +
281 p_outpic->p[i_index].i_visible_lines *
282 p_outpic->p[i_index].i_pitch;
286 for( ; p_out < p_out_end ; )
290 p_out_end -= p_outpic->p[i_index].i_pitch
291 - p_outpic->p[i_index].i_visible_pitch;
292 p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
295 for( ; p_in < p_line_end ; )
297 p_line_end -= i_pitch;
298 *(--p_out_end) = *p_line_end;
304 else /* i_index == 1 or 2 */
306 for( ; p_out < p_out_end ; )
308 uint8_t *p_line_end, *p_out_end2;
310 p_out_end -= p_outpic->p[i_index].i_pitch
311 - p_outpic->p[i_index].i_visible_pitch;
312 p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
313 p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
316 for( ; p_in < p_line_end ; )
320 p_line_end -= i_pitch;
322 p_line_end -= i_pitch;
325 /* Trick for (x+y)/2 without overflow, based on
326 * x + y == (x ^ y) + 2 * (x & y) */
327 *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
328 *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
331 p_out_end = p_out_end2;
338 case TRANSFORM_MODE_270:
339 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
341 int i_pitch = p_pic->p[i_index].i_pitch;
343 uint8_t *p_in = p_pic->p[i_index].p_pixels;
345 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
346 uint8_t *p_out_end = p_out +
347 p_outpic->p[i_index].i_visible_lines *
348 p_outpic->p[i_index].i_pitch;
352 for( ; p_out < p_out_end ; )
356 p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
359 for( ; p_in < p_in_end ; )
362 *p_out++ = *p_in_end;
365 p_out += p_outpic->p[i_index].i_pitch
366 - p_outpic->p[i_index].i_visible_pitch;
370 else /* i_index == 1 or 2 */
372 for( ; p_out < p_out_end ; )
374 uint8_t *p_in_end, *p_out2;
376 p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
378 p_out2 = p_out + p_outpic->p[i_index].i_pitch;
380 for( ; p_in < p_in_end ; )
389 /* Trick for (x+y)/2 without overflow, based on
390 * x + y == (x ^ y) + 2 * (x & y) */
391 *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
392 *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
395 p_out2 += p_outpic->p[i_index].i_pitch
396 - p_outpic->p[i_index].i_visible_pitch;
409 static void FilterYUYV( vout_thread_t *p_vout,
410 const picture_t *p_pic, picture_t *p_outpic )
413 int i_y_offset, i_u_offset, i_v_offset;
414 if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
415 &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
418 switch( p_vout->p_sys->i_mode )
420 case TRANSFORM_MODE_VFLIP:
421 /* Fall back on the default implementation */
422 FilterPlanar( p_vout, p_pic, p_outpic );
425 case TRANSFORM_MODE_90:
426 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
428 int i_pitch = p_pic->p[i_index].i_pitch;
430 uint8_t *p_in = p_pic->p[i_index].p_pixels;
432 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
433 uint8_t *p_out_end = p_out +
434 p_outpic->p[i_index].i_visible_lines *
435 p_outpic->p[i_index].i_pitch;
437 int i_offset = i_u_offset;
438 int i_offset2 = i_v_offset;
439 for( ; p_out < p_out_end ; )
443 p_out_end -= p_outpic->p[i_index].i_pitch
444 - p_outpic->p[i_index].i_visible_pitch;
445 p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
448 for( ; p_in < p_line_end ; )
450 p_line_end -= i_pitch;
452 p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
453 p_out_end[i_u_offset] = p_line_end[i_offset];
454 p_line_end -= i_pitch;
455 p_out_end[i_y_offset] = p_line_end[i_y_offset];
456 p_out_end[i_v_offset] = p_line_end[i_offset2];
463 i_offset = i_offset2;
470 case TRANSFORM_MODE_180:
471 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
473 uint8_t *p_in = p_pic->p[i_index].p_pixels;
474 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
475 * p_pic->p[i_index].i_pitch;
477 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
479 for( ; p_in < p_in_end ; )
481 uint8_t *p_line_start = p_in_end
482 - p_pic->p[i_index].i_pitch;
483 p_in_end -= p_pic->p[i_index].i_pitch
484 - p_pic->p[i_index].i_visible_pitch;
486 for( ; p_line_start < p_in_end ; )
489 p_out[i_y_offset] = p_in_end[i_y_offset+2];
490 p_out[i_u_offset] = p_in_end[i_u_offset];
491 p_out[i_y_offset+2] = p_in_end[i_y_offset];
492 p_out[i_v_offset] = p_in_end[i_v_offset];
496 p_out += p_outpic->p[i_index].i_pitch
497 - p_outpic->p[i_index].i_visible_pitch;
502 case TRANSFORM_MODE_270:
503 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
505 int i_pitch = p_pic->p[i_index].i_pitch;
507 uint8_t *p_in = p_pic->p[i_index].p_pixels;
509 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
510 uint8_t *p_out_end = p_out +
511 p_outpic->p[i_index].i_visible_lines *
512 p_outpic->p[i_index].i_pitch;
514 int i_offset = i_u_offset;
515 int i_offset2 = i_v_offset;
516 for( ; p_out < p_out_end ; )
521 + p_pic->p[i_index].i_visible_lines * i_pitch;
523 for( ; p_in < p_in_end ; )
526 p_out[i_y_offset] = p_in_end[i_y_offset];
527 p_out[i_u_offset] = p_in_end[i_offset];
529 p_out[i_y_offset+2] = p_in_end[i_y_offset];
530 p_out[i_v_offset] = p_in_end[i_offset2];
534 p_out += p_outpic->p[i_index].i_pitch
535 - p_outpic->p[i_index].i_visible_pitch;
540 i_offset = i_offset2;
547 case TRANSFORM_MODE_HFLIP:
548 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
550 uint8_t *p_in = p_pic->p[i_index].p_pixels;
551 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
552 * p_pic->p[i_index].i_pitch;
554 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
556 for( ; p_in < p_in_end ; )
558 uint8_t *p_line_end = p_in
559 + p_pic->p[i_index].i_visible_pitch;
561 for( ; p_in < p_line_end ; )
564 p_out[i_y_offset] = p_line_end[i_y_offset+2];
565 p_out[i_u_offset] = p_line_end[i_u_offset];
566 p_out[i_y_offset+2] = p_line_end[i_y_offset];
567 p_out[i_v_offset] = p_line_end[i_v_offset];
571 p_in += p_pic->p[i_index].i_pitch;
572 p_out += p_outpic->p[i_index].i_pitch
573 - p_outpic->p[i_index].i_visible_pitch;