]> git.sesse.net Git - vlc/blob - modules/video_filter/transform.c
Qt: add the orange cone pixmap
[vlc] / 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 static void Planar(plane_t *dst, const plane_t *src, convert_t f)
104 {
105     for (int y = 0; y < dst->i_visible_lines; y++) {
106         for (int x = 0; x < dst->i_visible_pitch; x++) {
107             int sx, sy;
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];
110         }
111     }
112 }
113
114 #define PLANAR(f) \
115     static void Planar##f(plane_t *dst, const plane_t *src) { Planar(dst, src, f); }
116
117 PLANAR(HFlip)
118 PLANAR(VFlip)
119 PLANAR(R90)
120 PLANAR(R180)
121 PLANAR(R270)
122
123 typedef struct {
124     char      name[8];
125     bool      is_rotated;
126     convert_t convert;
127     convert_t iconvert;
128     void      (*planar)(plane_t *dst, const plane_t *src);
129 } transform_description_t;
130
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, },
137
138     { "", false, NULL, NULL, NULL, }
139 };
140
141 struct filter_sys_t {
142     const transform_description_t  *dsc;
143     const vlc_chroma_description_t *chroma;
144 };
145
146 static picture_t *Filter(filter_t *filter, picture_t *src)
147 {
148     filter_sys_t *sys = filter->p_sys;
149
150     picture_t *dst = filter_NewPicture(filter);
151     if (!dst) {
152         picture_Release(src);
153         return NULL;
154     }
155
156     const vlc_chroma_description_t *chroma = sys->chroma;
157     if (chroma->plane_count < 3) {
158         /* TODO */
159     } else {
160         for (unsigned i = 0; i < chroma->plane_count; i++)
161             sys->dsc->planar(&dst->p[i], &src->p[i]);
162     }
163
164     picture_CopyProperties(dst, src);
165     picture_Release(src);
166     return dst;
167 }
168
169 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
170                  const vlc_mouse_t *mold, const vlc_mouse_t *mnew)
171 {
172     VLC_UNUSED( mold );
173
174     const video_format_t          *fmt = &filter->fmt_out.video;
175     const transform_description_t *dsc = filter->p_sys->dsc;
176
177     *mouse = *mnew;
178     dsc->convert(&mouse->i_x, &mouse->i_y,
179                  fmt->i_visible_width, fmt->i_visible_height, mouse->i_x, mouse->i_y);
180     return VLC_SUCCESS;
181 }
182
183 static int Open(vlc_object_t *object)
184 {
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;
188
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 */
194         return VLC_EGENERIC;
195     }
196
197     filter_sys_t *sys = malloc(sizeof(*sys));
198     if (!sys)
199         return VLC_ENOMEM;
200
201     sys->chroma = chroma;
202
203     char *type_name = var_InheritString(filter, CFG_PREFIX"type");
204
205     sys->dsc = NULL;
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];
209     }
210     if (!sys->dsc) {
211         sys->dsc = &descriptions[0];
212         msg_Warn(filter, "No valid transform mode provided, using '%s'", sys->dsc->name);
213     }
214
215     free(type_name);
216
217     if (sys->dsc->is_rotated) {
218         if (!filter->b_allow_fmt_out_change) {
219             msg_Err(filter, "Format change is not allowed");
220             free(sys);
221             return VLC_EGENERIC;
222         }
223
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;
230     }
231
232     dst->i_x_offset       = INT_MAX;
233     dst->i_y_offset       = INT_MAX;
234     for (int i = 0; i < 2; i++) {
235         int tx, ty;
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));
242     }
243
244     filter->p_sys           = sys;
245     filter->pf_video_filter = Filter;
246     filter->pf_video_mouse  = Mouse;
247     return VLC_SUCCESS;
248 }
249
250 static void Close(vlc_object_t *object)
251 {
252     filter_t     *filter = (filter_t *)object;
253     filter_sys_t *sys    = filter->p_sys;
254
255     free(sys);
256 }
257
258 #if 0
259 static void FilterI422( vout_thread_t *p_vout,
260                         const picture_t *p_pic, picture_t *p_outpic )
261 {
262     int i_index;
263     switch( p_vout->p_sys->i_mode )
264     {
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 );
270             return;
271
272         case TRANSFORM_MODE_90:
273             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
274             {
275                 int i_pitch = p_pic->p[i_index].i_pitch;
276
277                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
278
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;
283
284                 if( i_index == 0 )
285                 {
286                     for( ; p_out < p_out_end ; )
287                     {
288                         uint8_t *p_line_end;
289
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 *
293                             i_pitch;
294
295                         for( ; p_in < p_line_end ; )
296                         {
297                             p_line_end -= i_pitch;
298                             *(--p_out_end) = *p_line_end;
299                         }
300
301                         p_in++;
302                     }
303                 }
304                 else /* i_index == 1 or 2 */
305                 {
306                     for( ; p_out < p_out_end ; )
307                     {
308                         uint8_t *p_line_end, *p_out_end2;
309
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 *
314                             i_pitch;
315
316                         for( ; p_in < p_line_end ; )
317                         {
318                             uint8_t p1, p2;
319
320                             p_line_end -= i_pitch;
321                             p1 = *p_line_end;
322                             p_line_end -= i_pitch;
323                             p2 = *p_line_end;
324
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);
329                         }
330
331                         p_out_end = p_out_end2;
332                         p_in++;
333                     }
334                 }
335             }
336             break;
337
338         case TRANSFORM_MODE_270:
339             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
340             {
341                 int i_pitch = p_pic->p[i_index].i_pitch;
342
343                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
344
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;
349
350                 if( i_index == 0 )
351                 {
352                     for( ; p_out < p_out_end ; )
353                     {
354                         uint8_t *p_in_end;
355
356                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
357                             i_pitch;
358
359                         for( ; p_in < p_in_end ; )
360                         {
361                             p_in_end -= i_pitch;
362                             *p_out++ = *p_in_end;
363                         }
364
365                         p_out += p_outpic->p[i_index].i_pitch
366                                   - p_outpic->p[i_index].i_visible_pitch;
367                         p_in++;
368                     }
369                 }
370                 else /* i_index == 1 or 2 */
371                 {
372                     for( ; p_out < p_out_end ; )
373                     {
374                         uint8_t *p_in_end, *p_out2;
375
376                         p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
377                             i_pitch;
378                         p_out2 = p_out + p_outpic->p[i_index].i_pitch;
379
380                         for( ; p_in < p_in_end ; )
381                         {
382                             uint8_t p1, p2;
383
384                             p_in_end -= i_pitch;
385                             p1 = *p_in_end;
386                             p_in_end -= i_pitch;
387                             p2 = *p_in_end;
388
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);
393                         }
394
395                         p_out2 += p_outpic->p[i_index].i_pitch
396                                    - p_outpic->p[i_index].i_visible_pitch;
397                         p_out = p_out2;
398                         p_in++;
399                     }
400                 }
401             }
402             break;
403
404         default:
405             break;
406     }
407 }
408
409 static void FilterYUYV( vout_thread_t *p_vout,
410                         const picture_t *p_pic, picture_t *p_outpic )
411 {
412     int i_index;
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 )
416         return;
417
418     switch( p_vout->p_sys->i_mode )
419     {
420         case TRANSFORM_MODE_VFLIP:
421             /* Fall back on the default implementation */
422             FilterPlanar( p_vout, p_pic, p_outpic );
423             return;
424
425         case TRANSFORM_MODE_90:
426             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
427             {
428                 int i_pitch = p_pic->p[i_index].i_pitch;
429
430                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
431
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;
436
437                 int i_offset  = i_u_offset;
438                 int i_offset2 = i_v_offset;
439                 for( ; p_out < p_out_end ; )
440                 {
441                     uint8_t *p_line_end;
442
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 *
446                         i_pitch;
447
448                     for( ; p_in < p_line_end ; )
449                     {
450                         p_line_end -= i_pitch;
451                         p_out_end -= 4;
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];
457                     }
458
459                     p_in += 2;
460
461                     {
462                         int a = i_offset;
463                         i_offset = i_offset2;
464                         i_offset2 = a;
465                     }
466                 }
467             }
468             break;
469
470         case TRANSFORM_MODE_180:
471             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
472             {
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;
476
477                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
478
479                 for( ; p_in < p_in_end ; )
480                 {
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;
485
486                     for( ; p_line_start < p_in_end ; )
487                     {
488                         p_in_end -= 4;
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];
493                         p_out += 4;
494                     }
495
496                     p_out += p_outpic->p[i_index].i_pitch
497                               - p_outpic->p[i_index].i_visible_pitch;
498                 }
499             }
500             break;
501
502         case TRANSFORM_MODE_270:
503             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
504             {
505                 int i_pitch = p_pic->p[i_index].i_pitch;
506
507                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
508
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;
513
514                 int i_offset  = i_u_offset;
515                 int i_offset2 = i_v_offset;
516                 for( ; p_out < p_out_end ; )
517                 {
518                     uint8_t *p_in_end;
519
520                     p_in_end = p_in
521                              + p_pic->p[i_index].i_visible_lines * i_pitch;
522
523                     for( ; p_in < p_in_end ; )
524                     {
525                         p_in_end -= i_pitch;
526                         p_out[i_y_offset] = p_in_end[i_y_offset];
527                         p_out[i_u_offset] = p_in_end[i_offset];
528                         p_in_end -= i_pitch;
529                         p_out[i_y_offset+2] = p_in_end[i_y_offset];
530                         p_out[i_v_offset] = p_in_end[i_offset2];
531                         p_out += 4;
532                     }
533
534                     p_out += p_outpic->p[i_index].i_pitch
535                            - p_outpic->p[i_index].i_visible_pitch;
536                     p_in += 2;
537
538                     {
539                         int a = i_offset;
540                         i_offset = i_offset2;
541                         i_offset2 = a;
542                     }
543                 }
544             }
545             break;
546
547         case TRANSFORM_MODE_HFLIP:
548             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
549             {
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;
553
554                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
555
556                 for( ; p_in < p_in_end ; )
557                 {
558                     uint8_t *p_line_end = p_in
559                                         + p_pic->p[i_index].i_visible_pitch;
560
561                     for( ; p_in < p_line_end ; )
562                     {
563                         p_line_end -= 4;
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];
568                         p_out += 4;
569                     }
570
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;
574                 }
575             }
576             break;
577
578         default:
579             break;
580     }
581 }
582 #endif