]> git.sesse.net Git - vlc/blob - modules/video_filter/transform.c
0e1ec782e03b757582563d2a5712159ff2a56fdf
[vlc] / modules / video_filter / transform.c
1 /*****************************************************************************
2  * transform.c : transform image module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32
33 #include "filter_common.h"
34
35 #define TRANSFORM_MODE_HFLIP   1
36 #define TRANSFORM_MODE_VFLIP   2
37 #define TRANSFORM_MODE_90      3
38 #define TRANSFORM_MODE_180     4
39 #define TRANSFORM_MODE_270     5
40
41 /*****************************************************************************
42  * Local prototypes
43  *****************************************************************************/
44 static int  Create    ( vlc_object_t * );
45 static void Destroy   ( vlc_object_t * );
46
47 static int  Init      ( vout_thread_t * );
48 static void End       ( vout_thread_t * );
49 static void Render    ( vout_thread_t *, picture_t * );
50
51 static int  SendEvents( vlc_object_t *, char const *,
52                         vlc_value_t, vlc_value_t, void * );
53
54 /*****************************************************************************
55  * Module descriptor
56  *****************************************************************************/
57 #define TYPE_TEXT N_("Transform type")
58 #define TYPE_LONGTEXT N_("One of '90', '180', '270', 'hflip' and 'vflip'")
59
60 static char *type_list[] = { "90", "180", "270", "hflip", "vflip" };
61 static char *type_list_text[] = { N_("Rotate by 90 degrees"),
62   N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
63   N_("Flip horizontally"), N_("Flip vertically") };
64
65 vlc_module_begin();
66     set_description( _("Video transformation filter") );
67     set_capability( "video filter", 0 );
68
69     add_string( "transform-type", "90", NULL,
70                           TYPE_TEXT, TYPE_LONGTEXT, VLC_FALSE);
71         change_string_list( type_list, type_list_text, 0);
72
73     add_shortcut( "transform" );
74     set_callbacks( Create, Destroy );
75 vlc_module_end();
76
77 /*****************************************************************************
78  * vout_sys_t: Transform video output method descriptor
79  *****************************************************************************
80  * This structure is part of the video output thread descriptor.
81  * It describes the Transform specific properties of an output thread.
82  *****************************************************************************/
83 struct vout_sys_t
84 {
85     int i_mode;
86     vlc_bool_t b_rotation;
87     vout_thread_t *p_vout;
88 };
89
90 /*****************************************************************************
91  * Control: control facility for the vout (forwards to child vout)
92  *****************************************************************************/
93 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
94 {
95     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
96 }
97
98 /*****************************************************************************
99  * Create: allocates Transform video thread output method
100  *****************************************************************************
101  * This function allocates and initializes a Transform vout method.
102  *****************************************************************************/
103 static int Create( vlc_object_t *p_this )
104 {
105     vout_thread_t *p_vout = (vout_thread_t *)p_this;
106     char *psz_method;
107
108     /* Allocate structure */
109     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
110     if( p_vout->p_sys == NULL )
111     {
112         msg_Err( p_vout, "out of memory" );
113         return VLC_ENOMEM;
114     }
115
116     p_vout->pf_init = Init;
117     p_vout->pf_end = End;
118     p_vout->pf_manage = NULL;
119     p_vout->pf_render = Render;
120     p_vout->pf_display = NULL;
121     p_vout->pf_control = Control;
122
123     /* Look what method was requested */
124     psz_method = config_GetPsz( p_vout, "transform-type" );
125
126     if( psz_method == NULL )
127     {
128         msg_Err( p_vout, "configuration variable %s empty", "transform-type" );
129         msg_Err( p_vout, "no valid transform mode provided, using '90'" );
130         p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
131         p_vout->p_sys->b_rotation = 1;
132     }
133     else
134     {
135         if( !strcmp( psz_method, "hflip" ) )
136         {
137             p_vout->p_sys->i_mode = TRANSFORM_MODE_HFLIP;
138             p_vout->p_sys->b_rotation = 0;
139         }
140         else if( !strcmp( psz_method, "vflip" ) )
141         {
142             p_vout->p_sys->i_mode = TRANSFORM_MODE_VFLIP;
143             p_vout->p_sys->b_rotation = 0;
144         }
145         else if( !strcmp( psz_method, "90" ) )
146         {
147             p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
148             p_vout->p_sys->b_rotation = 1;
149         }
150         else if( !strcmp( psz_method, "180" ) )
151         {
152             p_vout->p_sys->i_mode = TRANSFORM_MODE_180;
153             p_vout->p_sys->b_rotation = 0;
154         }
155         else if( !strcmp( psz_method, "270" ) )
156         {
157             p_vout->p_sys->i_mode = TRANSFORM_MODE_270;
158             p_vout->p_sys->b_rotation = 1;
159         }
160         else
161         {
162             msg_Err( p_vout, "no valid transform mode provided, using '90'" );
163             p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
164             p_vout->p_sys->b_rotation = 1;
165         }
166
167         free( psz_method );
168     }
169
170     return VLC_SUCCESS;
171 }
172
173 /*****************************************************************************
174  * Init: initialize Transform video thread output method
175  *****************************************************************************/
176 static int Init( vout_thread_t *p_vout )
177 {
178     int i_index;
179     picture_t *p_pic;
180
181     I_OUTPUTPICTURES = 0;
182
183     /* Initialize the output structure */
184     p_vout->output.i_chroma = p_vout->render.i_chroma;
185     p_vout->output.i_width  = p_vout->render.i_width;
186     p_vout->output.i_height = p_vout->render.i_height;
187     p_vout->output.i_aspect = p_vout->render.i_aspect;
188
189     /* Try to open the real video output */
190     msg_Dbg( p_vout, "spawning the real video output" );
191
192     if( p_vout->p_sys->b_rotation )
193     {
194         p_vout->p_sys->p_vout = vout_Create( p_vout,
195                            p_vout->render.i_height, p_vout->render.i_width,
196                            p_vout->render.i_chroma,
197                            (uint64_t)VOUT_ASPECT_FACTOR
198                             * (uint64_t)VOUT_ASPECT_FACTOR
199                             / (uint64_t)p_vout->render.i_aspect );
200     }
201     else
202     {
203         p_vout->p_sys->p_vout = vout_Create( p_vout,
204                            p_vout->render.i_width, p_vout->render.i_height,
205                            p_vout->render.i_chroma, p_vout->render.i_aspect );
206     }
207
208     /* Everything failed */
209     if( p_vout->p_sys->p_vout == NULL )
210     {
211         msg_Err( p_vout, "cannot open vout, aborting" );
212         return VLC_EGENERIC;
213     }
214
215     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
216
217     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
218
219     ADD_PARENT_CALLBACKS( SendEventsToChild );
220
221     return VLC_SUCCESS;
222 }
223
224 /*****************************************************************************
225  * End: terminate Transform video thread output method
226  *****************************************************************************/
227 static void End( vout_thread_t *p_vout )
228 {
229     int i_index;
230
231     /* Free the fake output buffers we allocated */
232     for( i_index = I_OUTPUTPICTURES ; i_index ; )
233     {
234         i_index--;
235         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
236     }
237 }
238
239 /*****************************************************************************
240  * Destroy: destroy Transform video thread output method
241  *****************************************************************************
242  * Terminate an output method created by TransformCreateOutputMethod
243  *****************************************************************************/
244 static void Destroy( vlc_object_t *p_this )
245 {
246     vout_thread_t *p_vout = (vout_thread_t *)p_this;
247
248     if( p_vout->p_sys->p_vout )
249     {
250         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
251         vlc_object_detach( p_vout->p_sys->p_vout );
252         vout_Destroy( p_vout->p_sys->p_vout );
253     }
254
255     DEL_PARENT_CALLBACKS( SendEventsToChild );
256
257     free( p_vout->p_sys );
258 }
259
260 /*****************************************************************************
261  * Render: displays previously rendered output
262  *****************************************************************************
263  * This function send the currently rendered image to Transform image, waits
264  * until it is displayed and switch the two rendering buffers, preparing next
265  * frame.
266  *****************************************************************************/
267 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
268 {
269     picture_t *p_outpic;
270     int i_index;
271
272     /* This is a new frame. Get a structure from the video_output. */
273     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
274               == NULL )
275     {
276         if( p_vout->b_die || p_vout->b_error )
277         {
278             return;
279         }
280         msleep( VOUT_OUTMEM_SLEEP );
281     }
282
283     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
284     vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
285
286     switch( p_vout->p_sys->i_mode )
287     {
288         case TRANSFORM_MODE_90:
289             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
290             {
291                 int i_pitch = p_pic->p[i_index].i_pitch;
292
293                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
294
295                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
296                 uint8_t *p_out_end = p_out +
297                     p_outpic->p[i_index].i_visible_lines *
298                     p_outpic->p[i_index].i_pitch;
299
300                 for( ; p_out < p_out_end ; )
301                 {
302                     uint8_t *p_line_end;
303
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 *
307                         i_pitch;
308
309                     for( ; p_in < p_line_end ; )
310                     {
311                         p_line_end -= i_pitch;
312                         *(--p_out_end) = *p_line_end;
313                     }
314
315                     p_in++;
316                 }
317             }
318             break;
319
320         case TRANSFORM_MODE_180:
321             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
322             {
323                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
324                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
325                                             * p_pic->p[i_index].i_pitch;
326
327                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
328
329                 for( ; p_in < p_in_end ; )
330                 {
331                     uint8_t *p_line_start = p_in_end
332                                              - p_pic->p[i_index].i_pitch;
333                     p_in_end -= p_pic->p[i_index].i_pitch
334                                  - p_pic->p[i_index].i_visible_pitch;
335
336                     for( ; p_line_start < p_in_end ; )
337                     {
338                         *p_out++ = *(--p_in_end);
339                     }
340
341                     p_out += p_outpic->p[i_index].i_pitch
342                               - p_outpic->p[i_index].i_visible_pitch;
343                 }
344             }
345             break;
346
347         case TRANSFORM_MODE_270:
348             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
349             {
350                 int i_pitch = p_pic->p[i_index].i_pitch;
351
352                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
353
354                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
355                 uint8_t *p_out_end = p_out +
356                     p_outpic->p[i_index].i_visible_lines *
357                     p_outpic->p[i_index].i_pitch;
358
359                 for( ; p_out < p_out_end ; )
360                 {
361                     uint8_t *p_in_end;
362
363                     p_in_end = p_in + p_pic->p[i_index].i_visible_lines *
364                         i_pitch;
365
366                     for( ; p_in < p_in_end ; )
367                     {
368                         p_in_end -= i_pitch;
369                         *p_out++ = *p_in_end;
370                     }
371
372                     p_out += p_outpic->p[i_index].i_pitch
373                               - p_outpic->p[i_index].i_visible_pitch;
374                     p_in++;
375                 }
376             }
377             break;
378
379         case TRANSFORM_MODE_HFLIP:
380             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
381             {
382                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
383                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
384                                             * p_pic->p[i_index].i_pitch;
385
386                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
387
388                 for( ; p_in < p_in_end ; )
389                 {
390                     p_in_end -= p_pic->p[i_index].i_pitch;
391                     p_vout->p_vlc->pf_memcpy( p_out, p_in_end,
392                                            p_pic->p[i_index].i_visible_pitch );
393                     p_out += p_pic->p[i_index].i_pitch;
394                 }
395             }
396             break;
397
398         case TRANSFORM_MODE_VFLIP:
399             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
400             {
401                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
402                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_visible_lines
403                                             * p_pic->p[i_index].i_pitch;
404
405                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
406
407                 for( ; p_in < p_in_end ; )
408                 {
409                     uint8_t *p_line_end = p_in
410                                            + p_pic->p[i_index].i_visible_pitch;
411
412                     for( ; p_in < p_line_end ; )
413                     {
414                         *p_out++ = *(--p_line_end);
415                     }
416
417                     p_in += p_pic->p[i_index].i_pitch;
418                 }
419             }
420             break;
421
422         default:
423             break;
424     }
425
426     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
427
428     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
429 }
430
431 /*****************************************************************************
432  * SendEvents: forward mouse and keyboard events to the parent p_vout
433  *****************************************************************************/
434 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
435                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
436 {
437     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
438     vlc_value_t sentval = newval;
439
440     /* Translate the mouse coordinates */
441     if( !strcmp( psz_var, "mouse-x" ) )
442     {
443         switch( p_vout->p_sys->i_mode )
444         {
445         case TRANSFORM_MODE_270:
446             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
447                              - sentval.i_int;
448         case TRANSFORM_MODE_90:
449             var_Set( p_vout, "mouse-y", sentval );
450             return VLC_SUCCESS;
451
452         case TRANSFORM_MODE_180:
453         case TRANSFORM_MODE_HFLIP:
454             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
455                              - sentval.i_int;
456             break;
457
458         case TRANSFORM_MODE_VFLIP:
459         default:
460             break;
461         }
462     }
463     else if( !strcmp( psz_var, "mouse-y" ) )
464     {
465         switch( p_vout->p_sys->i_mode )
466         {
467         case TRANSFORM_MODE_90:
468             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
469                              - sentval.i_int;
470         case TRANSFORM_MODE_270:
471             var_Set( p_vout, "mouse-x", sentval );
472             return VLC_SUCCESS;
473
474         case TRANSFORM_MODE_180:
475         case TRANSFORM_MODE_VFLIP:
476             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
477                              - sentval.i_int;
478             break;
479
480         case TRANSFORM_MODE_HFLIP:
481         default:
482             break;
483         }
484     }
485
486     var_Set( p_vout, psz_var, sentval );
487
488     return VLC_SUCCESS;
489 }
490
491 /*****************************************************************************
492  * SendEventsToChild: forward events to the child/children vout
493  *****************************************************************************/
494 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
495                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
496 {
497     vout_thread_t *p_vout = (vout_thread_t *)p_this;
498     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
499     return VLC_SUCCESS;
500 }