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 static const char * const type_list[] = { "90", "180", "270", "hflip", "vflip" };
49 static const char * const type_list_text[] = { N_("Rotate by 90 degrees"),
50 N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
51 N_("Flip horizontally"), N_("Flip vertically") };
54 set_description(N_("Video transformation filter"))
55 set_shortname(N_("Transformation"))
56 set_help(N_("Rotate or flip the video"))
57 set_capability("video filter2", 0)
58 set_category(CAT_VIDEO)
59 set_subcategory(SUBCAT_VIDEO_VFILTER)
61 add_string(CFG_PREFIX "type", "90", TYPE_TEXT, TYPE_TEXT, false)
62 change_string_list(type_list, type_list_text, 0)
64 add_shortcut("transform")
65 set_callbacks(Open, Close)
68 /*****************************************************************************
70 *****************************************************************************/
71 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
77 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
83 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
89 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
94 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
100 typedef void (*convert_t)(int *, int *, int, int, int, int);
103 static void Planar##f(plane_t *restrict dst, const plane_t *restrict src) \
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] = \
110 src->p_pixels[sy * src->i_pitch + sx]; \
126 void (*planar)(plane_t *dst, const plane_t *src);
127 } transform_description_t;
129 static const transform_description_t descriptions[] = {
130 { "90", true, R90, R270, PlanarR90, },
131 { "180", false, R180, R180, PlanarR180, },
132 { "270", true, R270, R90, PlanarR270, },
133 { "hflip", false, HFlip, HFlip, PlanarHFlip, },
134 { "vflip", false, VFlip, VFlip, PlanarVFlip, },
136 { "", false, NULL, NULL, NULL, }
139 struct filter_sys_t {
140 const transform_description_t *dsc;
141 const vlc_chroma_description_t *chroma;
144 static picture_t *Filter(filter_t *filter, picture_t *src)
146 filter_sys_t *sys = filter->p_sys;
148 picture_t *dst = filter_NewPicture(filter);
150 picture_Release(src);
154 const vlc_chroma_description_t *chroma = sys->chroma;
155 if (chroma->plane_count < 3) {
158 for (unsigned i = 0; i < chroma->plane_count; i++)
159 sys->dsc->planar(&dst->p[i], &src->p[i]);
162 picture_CopyProperties(dst, src);
163 picture_Release(src);
167 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
168 const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
172 const video_format_t *fmt = &filter->fmt_out.video;
173 const transform_description_t *dsc = filter->p_sys->dsc;
176 dsc->convert(&mouse->i_x, &mouse->i_y,
177 fmt->i_visible_width, fmt->i_visible_height, mouse->i_x, mouse->i_y);
181 static bool SupportedChroma(const vlc_chroma_description_t *chroma)
186 if (chroma->pixel_size != 1)
189 for (unsigned i = 0; i < chroma->plane_count; i++)
190 if (chroma->p[i].w.num * chroma->p[i].h.den
191 != chroma->p[i].h.num * chroma->p[i].w.den)
197 static int Open(vlc_object_t *object)
199 filter_t *filter = (filter_t *)object;
200 const video_format_t *src = &filter->fmt_in.video;
201 video_format_t *dst = &filter->fmt_out.video;
203 const vlc_chroma_description_t *chroma =
204 vlc_fourcc_GetChromaDescription(src->i_chroma);
205 if (!SupportedChroma(chroma)) {
206 msg_Err(filter, "Unsupported chroma (%4.4s)", (char*)&src->i_chroma);
207 /* TODO support packed and rgb */
211 filter_sys_t *sys = malloc(sizeof(*sys));
215 sys->chroma = chroma;
217 char *type_name = var_InheritString(filter, CFG_PREFIX"type");
220 for (int i = 0; !sys->dsc && *descriptions[i].name; i++) {
221 if (type_name && *type_name && !strcmp(descriptions[i].name, type_name))
222 sys->dsc = &descriptions[i];
225 sys->dsc = &descriptions[0];
226 msg_Warn(filter, "No valid transform mode provided, using '%s'", sys->dsc->name);
231 if (sys->dsc->is_rotated) {
232 if (!filter->b_allow_fmt_out_change) {
233 msg_Err(filter, "Format change is not allowed");
238 dst->i_width = src->i_height;
239 dst->i_visible_width = src->i_visible_height;
240 dst->i_height = src->i_width;
241 dst->i_visible_height = src->i_visible_width;
242 dst->i_sar_num = src->i_sar_den;
243 dst->i_sar_den = src->i_sar_num;
246 dst->i_x_offset = INT_MAX;
247 dst->i_y_offset = INT_MAX;
248 for (int i = 0; i < 2; i++) {
250 sys->dsc->iconvert(&tx, &ty,
251 src->i_width, src->i_height,
252 src->i_x_offset + i * (src->i_visible_width - 1),
253 src->i_y_offset + i * (src->i_visible_height - 1));
254 dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
255 dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
259 filter->pf_video_filter = Filter;
260 filter->pf_video_mouse = Mouse;
264 static void Close(vlc_object_t *object)
266 filter_t *filter = (filter_t *)object;
267 filter_sys_t *sys = filter->p_sys;
273 static void FilterI422( vout_thread_t *p_vout,
274 const picture_t *p_pic, picture_t *p_outpic )
277 switch( p_vout->p_sys->i_mode )
279 case TRANSFORM_MODE_180:
280 case TRANSFORM_MODE_HFLIP:
281 case TRANSFORM_MODE_VFLIP:
282 /* Fall back on the default implementation */
283 FilterPlanar( p_vout, p_pic, p_outpic );
286 case TRANSFORM_MODE_90:
287 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
289 int i_pitch = p_pic->p[i_index].i_pitch;
291 uint8_t *p_in = p_pic->p[i_index].p_pixels;
293 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
294 uint8_t *p_out_end = p_out +
295 p_outpic->p[i_index].i_visible_lines *
296 p_outpic->p[i_index].i_pitch;
300 for( ; p_out < p_out_end ; )
304 p_out_end -= p_outpic->p[i_index].i_pitch
305 - p_outpic->p[i_index].i_visible_pitch;
306 p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
309 for( ; p_in < p_line_end ; )
311 p_line_end -= i_pitch;
312 *(--p_out_end) = *p_line_end;
318 else /* i_index == 1 or 2 */
320 for( ; p_out < p_out_end ; )
322 uint8_t *p_line_end, *p_out_end2;
324 p_out_end -= p_outpic->p[i_index].i_pitch
325 - p_outpic->p[i_index].i_visible_pitch;
326 p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
327 p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
330 for( ; p_in < p_line_end ; )
334 p_line_end -= i_pitch;
336 p_line_end -= i_pitch;
339 /* Trick for (x+y)/2 without overflow, based on
340 * x + y == (x ^ y) + 2 * (x & y) */
341 *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
342 *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
345 p_out_end = p_out_end2;
352 case TRANSFORM_MODE_270:
353 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
355 int i_pitch = p_pic->p[i_index].i_pitch;
357 uint8_t *p_in = p_pic->p[i_index].p_pixels;
359 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
360 uint8_t *p_out_end = p_out +
361 p_outpic->p[i_index].i_visible_lines *
362 p_outpic->p[i_index].i_pitch;
366 for( ; p_out < p_out_end ; )
370 p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
373 for( ; p_in < p_in_end ; )
376 *p_out++ = *p_in_end;
379 p_out += p_outpic->p[i_index].i_pitch
380 - p_outpic->p[i_index].i_visible_pitch;
384 else /* i_index == 1 or 2 */
386 for( ; p_out < p_out_end ; )
388 uint8_t *p_in_end, *p_out2;
390 p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
392 p_out2 = p_out + p_outpic->p[i_index].i_pitch;
394 for( ; p_in < p_in_end ; )
403 /* Trick for (x+y)/2 without overflow, based on
404 * x + y == (x ^ y) + 2 * (x & y) */
405 *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
406 *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
409 p_out2 += p_outpic->p[i_index].i_pitch
410 - p_outpic->p[i_index].i_visible_pitch;
423 static void FilterYUYV( vout_thread_t *p_vout,
424 const picture_t *p_pic, picture_t *p_outpic )
427 int i_y_offset, i_u_offset, i_v_offset;
428 if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
429 &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
432 switch( p_vout->p_sys->i_mode )
434 case TRANSFORM_MODE_VFLIP:
435 /* Fall back on the default implementation */
436 FilterPlanar( p_vout, p_pic, p_outpic );
439 case TRANSFORM_MODE_90:
440 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
442 int i_pitch = p_pic->p[i_index].i_pitch;
444 uint8_t *p_in = p_pic->p[i_index].p_pixels;
446 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
447 uint8_t *p_out_end = p_out +
448 p_outpic->p[i_index].i_visible_lines *
449 p_outpic->p[i_index].i_pitch;
451 int i_offset = i_u_offset;
452 int i_offset2 = i_v_offset;
453 for( ; p_out < p_out_end ; )
457 p_out_end -= p_outpic->p[i_index].i_pitch
458 - p_outpic->p[i_index].i_visible_pitch;
459 p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
462 for( ; p_in < p_line_end ; )
464 p_line_end -= i_pitch;
466 p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
467 p_out_end[i_u_offset] = p_line_end[i_offset];
468 p_line_end -= i_pitch;
469 p_out_end[i_y_offset] = p_line_end[i_y_offset];
470 p_out_end[i_v_offset] = p_line_end[i_offset2];
477 i_offset = i_offset2;
484 case TRANSFORM_MODE_180:
485 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
487 uint8_t *p_in = p_pic->p[i_index].p_pixels;
488 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
489 * p_pic->p[i_index].i_pitch;
491 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
493 for( ; p_in < p_in_end ; )
495 uint8_t *p_line_start = p_in_end
496 - p_pic->p[i_index].i_pitch;
497 p_in_end -= p_pic->p[i_index].i_pitch
498 - p_pic->p[i_index].i_visible_pitch;
500 for( ; p_line_start < p_in_end ; )
503 p_out[i_y_offset] = p_in_end[i_y_offset+2];
504 p_out[i_u_offset] = p_in_end[i_u_offset];
505 p_out[i_y_offset+2] = p_in_end[i_y_offset];
506 p_out[i_v_offset] = p_in_end[i_v_offset];
510 p_out += p_outpic->p[i_index].i_pitch
511 - p_outpic->p[i_index].i_visible_pitch;
516 case TRANSFORM_MODE_270:
517 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
519 int i_pitch = p_pic->p[i_index].i_pitch;
521 uint8_t *p_in = p_pic->p[i_index].p_pixels;
523 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
524 uint8_t *p_out_end = p_out +
525 p_outpic->p[i_index].i_visible_lines *
526 p_outpic->p[i_index].i_pitch;
528 int i_offset = i_u_offset;
529 int i_offset2 = i_v_offset;
530 for( ; p_out < p_out_end ; )
535 + p_pic->p[i_index].i_visible_lines * i_pitch;
537 for( ; p_in < p_in_end ; )
540 p_out[i_y_offset] = p_in_end[i_y_offset];
541 p_out[i_u_offset] = p_in_end[i_offset];
543 p_out[i_y_offset+2] = p_in_end[i_y_offset];
544 p_out[i_v_offset] = p_in_end[i_offset2];
548 p_out += p_outpic->p[i_index].i_pitch
549 - p_outpic->p[i_index].i_visible_pitch;
554 i_offset = i_offset2;
561 case TRANSFORM_MODE_HFLIP:
562 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
564 uint8_t *p_in = p_pic->p[i_index].p_pixels;
565 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
566 * p_pic->p[i_index].i_pitch;
568 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
570 for( ; p_in < p_in_end ; )
572 uint8_t *p_line_end = p_in
573 + p_pic->p[i_index].i_visible_pitch;
575 for( ; p_in < p_line_end ; )
578 p_out[i_y_offset] = p_line_end[i_y_offset+2];
579 p_out[i_u_offset] = p_line_end[i_u_offset];
580 p_out[i_y_offset+2] = p_line_end[i_y_offset];
581 p_out[i_v_offset] = p_line_end[i_v_offset];
585 p_in += p_pic->p[i_index].i_pitch;
586 p_out += p_outpic->p[i_index].i_pitch
587 - p_outpic->p[i_index].i_visible_pitch;