Fixed crop parameters in transform filter.
[vlc.git] / modules / video_filter / transform.c
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  * $Id$
7  *
8  * Authors: Samuel Hocevar <sam@zoy.org>
9  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 #   include "config.h"
32 #endif
33 #include <limits.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
44
45 #define CFG_PREFIX "transform-"
46
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") };
53
54 vlc_module_begin()
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)
61
62     add_string(CFG_PREFIX "type", "90", TYPE_TEXT, TYPE_LONGTEXT, false)
63         change_string_list(type_list, type_list_text, 0)
64
65     add_shortcut("transform")
66     set_callbacks(Open, Close)
67 vlc_module_end()
68
69 /*****************************************************************************
70  * Local prototypes
71  *****************************************************************************/
72 static void HFlip(int *sx, int *sy, int w, int h, int dx, int dy)
73 {
74     *sx = w - 1 - dx;
75     *sy = dy;
76 }
77 static void VFlip(int *sx, int *sy, int w, int h, int dx, int dy)
78 {
79     *sx = dx;
80     *sy = h - 1 - dy;
81 }
82 static void R90(int *sx, int *sy, int w, int h, int dx, int dy)
83 {
84     *sx = dy;
85     *sy = w - 1 - dx;
86 }
87 static void R180(int *sx, int *sy, int w, int h, int dx, int dy)
88 {
89     *sx = w - dx;
90     *sy = h - dy;
91 }
92 static void R270(int *sx, int *sy, int w, int h, int dx, int dy)
93 {
94     *sx = h - 1 - dy;
95     *sy = dx;
96 }
97 typedef void (*convert_t)(int *, int *, int, int, int, int);
98
99 static void Planar(plane_t *dst, const plane_t *src, convert_t f)
100 {
101     for (int y = 0; y < dst->i_visible_lines; y++) {
102         for (int x = 0; x < dst->i_visible_pitch; x++) {
103             int sx, sy;
104             f(&sx, &sy, dst->i_visible_pitch, dst->i_visible_lines, x, y);
105             dst->p_pixels[y * dst->i_pitch + x] = src->p_pixels[sy * src->i_pitch + sx];
106         }
107     }
108 }
109
110 #define PLANAR(f) \
111     static void Planar##f(plane_t *dst, const plane_t *src) { Planar(dst, src, f); }
112
113 PLANAR(HFlip)
114 PLANAR(VFlip)
115 PLANAR(R90)
116 PLANAR(R180)
117 PLANAR(R270)
118
119 typedef struct {
120     char      name[8];
121     bool      is_rotated;
122     convert_t convert;
123     convert_t iconvert;
124     void      (*planar)(plane_t *dst, const plane_t *src);
125 } transform_description_t;
126
127 static const transform_description_t descriptions[] = {
128     { "90",    true,  R90,   R270,  PlanarR90, },
129     { "180",   false, R180,  R180,  PlanarR180, },
130     { "270",   true,  R270,  R90,   PlanarR270, },
131     { "hflip", false, HFlip, HFlip, PlanarHFlip, },
132     { "vflip", false, VFlip, VFlip, PlanarVFlip, },
133
134     { "", false, NULL, NULL, NULL, }
135 };
136
137 struct filter_sys_t {
138     const transform_description_t  *dsc;
139     const vlc_chroma_description_t *chroma;
140 };
141
142 static picture_t *Filter(filter_t *filter, picture_t *src)
143 {
144     filter_sys_t *sys = filter->p_sys;
145
146     picture_t *dst = filter_NewPicture(filter);
147     if (!dst) {
148         picture_Release(src);
149         return NULL;
150     }
151
152     const vlc_chroma_description_t *chroma = sys->chroma;
153     if (chroma->plane_count < 3) {
154         /* TODO */
155     } else {
156         for (unsigned i = 0; i < chroma->plane_count; i++)
157             sys->dsc->planar(&dst->p[i], &src->p[i]);
158     }
159
160     picture_CopyProperties(dst, src);
161     picture_Release(src);
162     return dst;
163 }
164
165 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
166                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
167 {
168     const video_format_t          *fmt = &filter->fmt_out.video;
169     const transform_description_t *dsc = filter->p_sys->dsc;
170
171     *mouse = *mnew;
172     dsc->convert(&mouse->i_x, &mouse->i_y,
173                  fmt->i_visible_width, fmt->i_visible_height, mouse->i_x, mouse->i_y);
174     return VLC_SUCCESS;
175 }
176
177 static int Open(vlc_object_t *object)
178 {
179     filter_t *filter = (filter_t *)object;
180     const video_format_t *src = &filter->fmt_in.video;
181     video_format_t       *dst = &filter->fmt_out.video;
182
183     const vlc_chroma_description_t *chroma =
184         vlc_fourcc_GetChromaDescription(src->i_chroma);
185     if (!chroma || chroma->plane_count < 3) {
186         msg_Err(filter, "Unsupported chroma (%4.4s)", (char*)&src->i_chroma);
187         /* TODO support packed and rgb */
188         return VLC_EGENERIC;
189     }
190
191     filter_sys_t *sys = malloc(sizeof(*sys));
192     if (!sys)
193         return VLC_ENOMEM;
194
195     sys->chroma = chroma;
196
197     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
198
199     sys->dsc = NULL;
200     for (int i = 0; !sys->dsc && *descriptions[i].name; i++) {
201         if (type_name && *type_name && !strcmp(descriptions[i].name, type_name))
202             sys->dsc = &descriptions[i];
203     }
204     if (!sys->dsc) {
205         sys->dsc = &descriptions[0];
206         msg_Warn(filter, "No valid transform mode provided, using '%s'", sys->dsc->name);
207     }
208
209     free(type_name);
210
211     if (sys->dsc->is_rotated) {
212         if (!filter->b_allow_fmt_out_change) {
213             msg_Err(filter, "Format change is not allowed");
214             free(sys);
215             return VLC_EGENERIC;
216         }
217
218         dst->i_width          = src->i_height;
219         dst->i_visible_width  = src->i_visible_height;
220         dst->i_height         = src->i_width;
221         dst->i_visible_height = src->i_visible_width;
222         dst->i_sar_num        = src->i_sar_den;
223         dst->i_sar_den        = src->i_sar_num;
224     }
225
226     dst->i_x_offset       = INT_MAX;
227     dst->i_y_offset       = INT_MAX;
228     for (int i = 0; i < 2; i++) {
229         int tx, ty;
230         sys->dsc->iconvert(&tx, &ty,
231                            src->i_width, src->i_height,
232                            src->i_x_offset + i * (src->i_visible_width  - 1),
233                            src->i_y_offset + i * (src->i_visible_height - 1));
234         dst->i_x_offset = __MIN(dst->i_x_offset, (unsigned)(1 + tx));
235         dst->i_y_offset = __MIN(dst->i_y_offset, (unsigned)(1 + ty));
236     }
237
238     filter->p_sys           = sys;
239     filter->pf_video_filter = Filter;
240     filter->pf_video_mouse  = Mouse;
241     return VLC_SUCCESS;
242 }
243
244 static void Close(vlc_object_t *object)
245 {
246     filter_t     *filter = (filter_t *)object;
247     filter_sys_t *sys    = filter->p_sys;
248
249     free(sys);
250 }
251
252 #if 0
253 /*****************************************************************************
254  * Init: initialize Transform video thread output method
255  *****************************************************************************/
256 static int Init( vout_thread_t *p_vout )
257 {
258     video_format_t fmt;
259
260     I_OUTPUTPICTURES = 0;
261     memset( &fmt, 0, sizeof(video_format_t ) );
262
263     /* Initialize the output structure */
264     p_vout->output.i_chroma = p_vout->render.i_chroma;
265     p_vout->output.i_width  = p_vout->render.i_width;
266     p_vout->output.i_height = p_vout->render.i_height;
267     p_vout->output.i_aspect = p_vout->render.i_aspect;
268     p_vout->fmt_out = p_vout->fmt_in;
269     fmt = p_vout->fmt_out;
270
271     /* Try to open the real video output */
272     msg_Dbg( p_vout, "spawning the real video output" );
273
274     if( p_vout->p_sys->b_rotation )
275     {
276         fmt.i_width = p_vout->fmt_out.i_height;
277         fmt.i_visible_width = p_vout->fmt_out.i_visible_height;
278         fmt.i_x_offset = p_vout->fmt_out.i_y_offset;
279
280         fmt.i_height = p_vout->fmt_out.i_width;
281         fmt.i_visible_height = p_vout->fmt_out.i_visible_width;
282         fmt.i_y_offset = p_vout->fmt_out.i_x_offset;
283
284         fmt.i_sar_num = p_vout->fmt_out.i_sar_den;
285         fmt.i_sar_den = p_vout->fmt_out.i_sar_num;
286     }
287
288     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
289
290     /* Everything failed */
291     if( p_vout->p_sys->p_vout == NULL )
292     {
293         msg_Err( p_vout, "cannot open vout, aborting" );
294         return VLC_EGENERIC;
295     }
296
297     vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
298
299     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
300
301     return VLC_SUCCESS;
302 }
303 static void FilterI422( vout_thread_t *p_vout,
304                         const picture_t *p_pic, picture_t *p_outpic )
305 {
306     int i_index;
307     switch( p_vout->p_sys->i_mode )
308     {
309         case TRANSFORM_MODE_180:
310         case TRANSFORM_MODE_HFLIP:
311         case TRANSFORM_MODE_VFLIP:
312             /* Fall back on the default implementation */
313             FilterPlanar( p_vout, p_pic, p_outpic );
314             return;
315
316         case TRANSFORM_MODE_90:
317             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
318             {
319                 int i_pitch = p_pic->p[i_index].i_pitch;
320
321                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
322
323                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
324                 uint8_t *p_out_end = p_out +
325                     p_outpic->p[i_index].i_visible_lines *
326                     p_outpic->p[i_index].i_pitch;
327
328                 if( i_index == 0 )
329                 {
330                     for( ; p_out < p_out_end ; )
331                     {
332                         uint8_t *p_line_end;
333
334                         p_out_end -= p_outpic->p[i_index].i_pitch
335                                       - p_outpic->p[i_index].i_visible_pitch;
336                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
337                             i_pitch;
338
339                         for( ; p_in < p_line_end ; )
340                         {
341                             p_line_end -= i_pitch;
342                             *(--p_out_end) = *p_line_end;
343                         }
344
345                         p_in++;
346                     }
347                 }
348                 else /* i_index == 1 or 2 */
349                 {
350                     for( ; p_out < p_out_end ; )
351                     {
352                         uint8_t *p_line_end, *p_out_end2;
353
354                         p_out_end -= p_outpic->p[i_index].i_pitch
355                                       - p_outpic->p[i_index].i_visible_pitch;
356                         p_out_end2 = p_out_end - p_outpic->p[i_index].i_pitch;
357                         p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
358                             i_pitch;
359
360                         for( ; p_in < p_line_end ; )
361                         {
362                             uint8_t p1, p2;
363
364                             p_line_end -= i_pitch;
365                             p1 = *p_line_end;
366                             p_line_end -= i_pitch;
367                             p2 = *p_line_end;
368
369                             /* Trick for (x+y)/2 without overflow, based on
370                              *   x + y == (x ^ y) + 2 * (x & y) */
371                             *(--p_out_end) = (p1 & p2) + ((p1 ^ p2) / 2);
372                             *(--p_out_end2) = (p1 & p2) + ((p1 ^ p2) / 2);
373                         }
374
375                         p_out_end = p_out_end2;
376                         p_in++;
377                     }
378                 }
379             }
380             break;
381
382         case TRANSFORM_MODE_270:
383             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
384             {
385                 int i_pitch = p_pic->p[i_index].i_pitch;
386
387                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
388
389                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
390                 uint8_t *p_out_end = p_out +
391                     p_outpic->p[i_index].i_visible_lines *
392                     p_outpic->p[i_index].i_pitch;
393
394                 if( i_index == 0 )
395                 {
396                     for( ; p_out < p_out_end ; )
397                     {
398                         uint8_t *p_in_end;
399
400                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
401                             i_pitch;
402
403                         for( ; p_in < p_in_end ; )
404                         {
405                             p_in_end -= i_pitch;
406                             *p_out++ = *p_in_end;
407                         }
408
409                         p_out += p_outpic->p[i_index].i_pitch
410                                   - p_outpic->p[i_index].i_visible_pitch;
411                         p_in++;
412                     }
413                 }
414                 else /* i_index == 1 or 2 */
415                 {
416                     for( ; p_out < p_out_end ; )
417                     {
418                         uint8_t *p_in_end, *p_out2;
419
420                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
421                             i_pitch;
422                         p_out2 = p_out + p_outpic->p[i_index].i_pitch;
423
424                         for( ; p_in < p_in_end ; )
425                         {
426                             uint8_t p1, p2;
427
428                             p_in_end -= i_pitch;
429                             p1 = *p_in_end;
430                             p_in_end -= i_pitch;
431                             p2 = *p_in_end;
432
433                             /* Trick for (x+y)/2 without overflow, based on
434                              *   x + y == (x ^ y) + 2 * (x & y) */
435                             *p_out++ = (p1 & p2) + ((p1 ^ p2) / 2);
436                             *p_out2++ = (p1 & p2) + ((p1 ^ p2) / 2);
437                         }
438
439                         p_out2 += p_outpic->p[i_index].i_pitch
440                                    - p_outpic->p[i_index].i_visible_pitch;
441                         p_out = p_out2;
442                         p_in++;
443                     }
444                 }
445             }
446             break;
447
448         default:
449             break;
450     }
451 }
452
453 static void FilterYUYV( vout_thread_t *p_vout,
454                         const picture_t *p_pic, picture_t *p_outpic )
455 {
456     int i_index;
457     int i_y_offset, i_u_offset, i_v_offset;
458     if( GetPackedYuvOffsets( p_pic->format.i_chroma, &i_y_offset,
459                              &i_u_offset, &i_v_offset ) != VLC_SUCCESS )
460         return;
461
462     switch( p_vout->p_sys->i_mode )
463     {
464         case TRANSFORM_MODE_VFLIP:
465             /* Fall back on the default implementation */
466             FilterPlanar( p_vout, p_pic, p_outpic );
467             return;
468
469         case TRANSFORM_MODE_90:
470             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
471             {
472                 int i_pitch = p_pic->p[i_index].i_pitch;
473
474                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
475
476                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
477                 uint8_t *p_out_end = p_out +
478                     p_outpic->p[i_index].i_visible_lines *
479                     p_outpic->p[i_index].i_pitch;
480
481                 int i_offset  = i_u_offset;
482                 int i_offset2 = i_v_offset;
483                 for( ; p_out < p_out_end ; )
484                 {
485                     uint8_t *p_line_end;
486
487                     p_out_end -= p_outpic->p[i_index].i_pitch
488                                   - p_outpic->p[i_index].i_visible_pitch;
489                     p_line_end = p_in + p_pic->p[i_index].i_visible_lines *
490                         i_pitch;
491
492                     for( ; p_in < p_line_end ; )
493                     {
494                         p_line_end -= i_pitch;
495                         p_out_end -= 4;
496                         p_out_end[i_y_offset+2] = p_line_end[i_y_offset];
497                         p_out_end[i_u_offset] = p_line_end[i_offset];
498                         p_line_end -= i_pitch;
499                         p_out_end[i_y_offset] = p_line_end[i_y_offset];
500                         p_out_end[i_v_offset] = p_line_end[i_offset2];
501                     }
502
503                     p_in += 2;
504
505                     {
506                         int a = i_offset;
507                         i_offset = i_offset2;
508                         i_offset2 = a;
509                     }
510                 }
511             }
512             break;
513
514         case TRANSFORM_MODE_180:
515             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
516             {
517                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
518                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
519                                             * p_pic->p[i_index].i_pitch;
520
521                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
522
523                 for( ; p_in < p_in_end ; )
524                 {
525                     uint8_t *p_line_start = p_in_end
526                                              - p_pic->p[i_index].i_pitch;
527                     p_in_end -= p_pic->p[i_index].i_pitch
528                                  - p_pic->p[i_index].i_visible_pitch;
529
530                     for( ; p_line_start < p_in_end ; )
531                     {
532                         p_in_end -= 4;
533                         p_out[i_y_offset] = p_in_end[i_y_offset+2];
534                         p_out[i_u_offset] = p_in_end[i_u_offset];
535                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
536                         p_out[i_v_offset] = p_in_end[i_v_offset];
537                         p_out += 4;
538                     }
539
540                     p_out += p_outpic->p[i_index].i_pitch
541                               - p_outpic->p[i_index].i_visible_pitch;
542                 }
543             }
544             break;
545
546         case TRANSFORM_MODE_270:
547             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
548             {
549                 int i_pitch = p_pic->p[i_index].i_pitch;
550
551                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
552
553                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
554                 uint8_t *p_out_end = p_out +
555                     p_outpic->p[i_index].i_visible_lines *
556                     p_outpic->p[i_index].i_pitch;
557
558                 int i_offset  = i_u_offset;
559                 int i_offset2 = i_v_offset;
560                 for( ; p_out < p_out_end ; )
561                 {
562                     uint8_t *p_in_end;
563
564                     p_in_end = p_in
565                              + p_pic->p[i_index].i_visible_lines * i_pitch;
566
567                     for( ; p_in < p_in_end ; )
568                     {
569                         p_in_end -= i_pitch;
570                         p_out[i_y_offset] = p_in_end[i_y_offset];
571                         p_out[i_u_offset] = p_in_end[i_offset];
572                         p_in_end -= i_pitch;
573                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
574                         p_out[i_v_offset] = p_in_end[i_offset2];
575                         p_out += 4;
576                     }
577
578                     p_out += p_outpic->p[i_index].i_pitch
579                            - p_outpic->p[i_index].i_visible_pitch;
580                     p_in += 2;
581
582                     {
583                         int a = i_offset;
584                         i_offset = i_offset2;
585                         i_offset2 = a;
586                     }
587                 }
588             }
589             break;
590
591         case TRANSFORM_MODE_HFLIP:
592             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
593             {
594                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
595                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
596                                          * p_pic->p[i_index].i_pitch;
597
598                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
599
600                 for( ; p_in < p_in_end ; )
601                 {
602                     uint8_t *p_line_end = p_in
603                                         + p_pic->p[i_index].i_visible_pitch;
604
605                     for( ; p_in < p_line_end ; )
606                     {
607                         p_line_end -= 4;
608                         p_out[i_y_offset] = p_line_end[i_y_offset+2];
609                         p_out[i_u_offset] = p_line_end[i_u_offset];
610                         p_out[i_y_offset+2] = p_line_end[i_y_offset];
611                         p_out[i_v_offset] = p_line_end[i_v_offset];
612                         p_out += 4;
613                     }
614
615                     p_in += p_pic->p[i_index].i_pitch;
616                     p_out += p_outpic->p[i_index].i_pitch
617                                 - p_outpic->p[i_index].i_visible_pitch;
618                 }
619             }
620             break;
621
622         default:
623             break;
624     }
625 }
626 #endif