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