]> git.sesse.net Git - vlc/blob - modules/video_filter/magnify.c
7e6130663c4eb6f4ced1d9cca5f843b59a125d1d
[vlc] / modules / video_filter / magnify.c
1 /*****************************************************************************
2  * magnify.c : Magnify/Zoom interactive effect
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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 <math.h>
34
35 #include "filter_common.h"
36 #include "vlc_image.h"
37 #include "vlc_input.h"
38 #include "vlc_playlist.h"
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  Create    ( vlc_object_t * );
44 static void Destroy   ( vlc_object_t * );
45
46 static int  Init      ( vout_thread_t * );
47 static void End       ( vout_thread_t * );
48 static void Render    ( vout_thread_t *, picture_t * );
49
50 static int  SendEvents   ( vlc_object_t *, char const *,
51                            vlc_value_t, vlc_value_t, void * );
52 static int  MouseEvent   ( vlc_object_t *, char const *,
53                            vlc_value_t, vlc_value_t, void * );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58 vlc_module_begin();
59     set_description( _("Magnify/Zoom interactive video filter") );
60     set_shortname( _( "Magnify" ));
61     set_capability( "video filter", 0 );
62     set_category( CAT_VIDEO );
63     set_subcategory( SUBCAT_VIDEO_VFILTER );
64
65     set_callbacks( Create, Destroy );
66 vlc_module_end();
67
68 /*****************************************************************************
69  * vout_sys_t: Magnify video output method descriptor
70  *****************************************************************************/
71 struct vout_sys_t
72 {
73     vout_thread_t *p_vout;
74
75     image_handler_t *p_image;
76
77     int i_zoom; /* zoom level in percent */
78     int i_x, i_y; /* top left corner coordinates in original image */
79
80     vlc_bool_t b_visible; /* is "interface" visible ? */
81 };
82
83 /*****************************************************************************
84  * Control: control facility for the vout (forwards to child vout)
85  *****************************************************************************/
86 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
87 {
88     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
89 }
90
91 /*****************************************************************************
92  * Create: allocates Magnify video thread output method
93  *****************************************************************************/
94 static int Create( vlc_object_t *p_this )
95 {
96     vout_thread_t *p_vout = (vout_thread_t *)p_this;
97
98     /* Allocate structure */
99     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
100     if( p_vout->p_sys == NULL )
101     {
102         msg_Err( p_vout, "out of memory" );
103         return VLC_ENOMEM;
104     }
105
106     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
107
108     p_vout->pf_init = Init;
109     p_vout->pf_end = End;
110     p_vout->pf_manage = NULL;
111     p_vout->pf_render = Render;
112     p_vout->pf_display = NULL;
113     p_vout->pf_control = Control;
114
115     return VLC_SUCCESS;
116 }
117
118 /*****************************************************************************
119  * Init: initialize Magnify video thread output method
120  *****************************************************************************/
121 static int Init( vout_thread_t *p_vout )
122 {
123     int i_index;
124     picture_t *p_pic;
125     video_format_t fmt;
126
127     memset( &fmt, 0, sizeof(video_format_t) );
128     I_OUTPUTPICTURES = 0;
129
130     /* Initialize the output structure */
131     p_vout->output.i_chroma = p_vout->render.i_chroma;
132     p_vout->output.i_width  = p_vout->render.i_width;
133     p_vout->output.i_height = p_vout->render.i_height;
134     p_vout->output.i_aspect = p_vout->render.i_aspect;
135
136     p_vout->fmt_out = p_vout->fmt_in;
137     fmt = p_vout->fmt_out;
138
139     /* Try to open the real video output */
140     msg_Dbg( p_vout, "spawning the real video output" );
141
142     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
143
144     /* Everything failed */
145     if( p_vout->p_sys->p_vout == NULL )
146     {
147         msg_Err( p_vout, "cannot open vout, aborting" );
148         return VLC_EGENERIC;
149     }
150
151 #define VIS_ZOOM 4
152     p_vout->p_sys->i_x = 0;
153     p_vout->p_sys->i_y = 0;
154 #define ZOOM_FACTOR 100
155     p_vout->p_sys->i_zoom = 200;
156     p_vout->p_sys->b_visible = VLC_TRUE;
157
158     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
159     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
160     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
161                      MouseEvent, p_vout);
162
163     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
164     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
165     ADD_PARENT_CALLBACKS( SendEventsToChild );
166
167     return VLC_SUCCESS;
168 }
169
170 /*****************************************************************************
171  * End: terminate Magnify video thread output method
172  *****************************************************************************/
173 static void End( vout_thread_t *p_vout )
174 {
175     int i_index;
176
177     /* Free the fake output buffers we allocated */
178     for( i_index = I_OUTPUTPICTURES ; i_index ; )
179     {
180         i_index--;
181         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
182     }
183
184     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
185     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
186     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
187 }
188
189 /*****************************************************************************
190  * Destroy: destroy Magnify video thread output method
191  *****************************************************************************/
192 static void Destroy( vlc_object_t *p_this )
193 {
194     vout_thread_t *p_vout = (vout_thread_t *)p_this;
195
196     if( p_vout->p_sys->p_vout )
197     {
198         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
199         vlc_object_detach( p_vout->p_sys->p_vout );
200         vout_Destroy( p_vout->p_sys->p_vout );
201     }
202
203     image_HandlerDelete( p_vout->p_sys->p_image );
204
205     DEL_PARENT_CALLBACKS( SendEventsToChild );
206
207     free( p_vout->p_sys );
208 }
209
210 /*****************************************************************************
211  * Render: displays previously rendered output
212  *****************************************************************************/
213 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
214 {
215     picture_t *p_outpic;
216
217     int o_x = p_vout->p_sys->i_x;
218     int o_y = p_vout->p_sys->i_y;
219     int o_zoom = p_vout->p_sys->i_zoom;
220     int x,y,o_yp,o_xp;
221     int v_w, v_h;
222     video_format_t fmt_out;
223     picture_t *p_converted;
224     plane_t *p_oyp=NULL;
225
226     memset( &fmt_out, 0, sizeof(video_format_t) );
227     /* This is a new frame. Get a structure from the video_output. */
228     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
229               == NULL )
230     {
231         if( p_vout->b_die || p_vout->b_error )
232         {
233             return;
234         }
235         msleep( VOUT_OUTMEM_SLEEP );
236     }
237
238     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
239
240     p_oyp = &(p_outpic->p[Y_PLANE]);
241
242     /* background magnified image */
243     if( o_zoom != ZOOM_FACTOR )
244     {
245 #define magnify( plane ) \
246     o_yp = o_y*p_outpic->p[plane].i_lines/p_outpic->p[Y_PLANE].i_lines; \
247     o_xp = o_x*p_outpic->p[plane].i_pitch/p_outpic->p[Y_PLANE].i_pitch; \
248     for( y=0; y<p_outpic->p[plane].i_visible_lines; y++ ) \
249     { \
250         for( x=0; x<p_outpic->p[plane].i_visible_pitch; x++ ) \
251         { \
252             p_outpic->p[plane].p_pixels[y*p_outpic->p[plane].i_pitch+x] = \
253                 p_pic->p[plane].p_pixels[ \
254                     ( o_yp + y*ZOOM_FACTOR/o_zoom )*p_outpic->p[plane].i_pitch \
255                     + o_xp + x*ZOOM_FACTOR/o_zoom \
256                 ]; \
257         } \
258     }
259     magnify( Y_PLANE );
260     magnify( U_PLANE );
261     magnify( V_PLANE );
262 #undef magnify
263     }
264     else
265     {
266 #define copy( plane ) \
267         p_vout->p_libvlc-> \
268         pf_memcpy( p_outpic->p[plane].p_pixels, p_pic->p[plane].p_pixels, \
269             p_outpic->p[plane].i_lines * p_outpic->p[plane].i_pitch );
270         copy( Y_PLANE );
271         copy( U_PLANE );
272         copy( V_PLANE );
273 #undef copy
274     }
275
276     if( p_vout->p_sys->b_visible )
277     {
278         /* image visualization */
279         fmt_out = p_vout->fmt_out;
280         fmt_out.i_width = p_vout->render.i_width/VIS_ZOOM;
281         fmt_out.i_height = p_vout->render.i_height/VIS_ZOOM;
282         p_converted = image_Convert( p_vout->p_sys->p_image, p_pic,
283                                      &(p_pic->format), &fmt_out );
284     #define copyimage( plane ) \
285         for( y=0; y<p_converted->p[plane].i_visible_lines; y++) \
286         { \
287             p_vout->p_libvlc->pf_memcpy( \
288             p_outpic->p[plane].p_pixels+y*p_outpic->p[plane].i_pitch, \
289             p_converted->p[plane].p_pixels+y*p_converted->p[plane].i_pitch, \
290             p_converted->p[plane].i_visible_pitch ); \
291         }
292         copyimage( Y_PLANE );
293         copyimage( U_PLANE );
294         copyimage( V_PLANE );
295     #undef copyimage
296         p_converted->pf_release( p_converted );
297
298         /* white rectangle on visualization */
299         v_w = p_oyp->i_pitch*ZOOM_FACTOR/(VIS_ZOOM*o_zoom);
300         v_h = (o_y+p_oyp->i_lines*ZOOM_FACTOR/o_zoom)/VIS_ZOOM;
301         /* top line */
302         p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
303                                      + o_y/VIS_ZOOM*p_oyp->i_pitch
304                                      + o_x/VIS_ZOOM, 0xff, v_w+1 );
305
306         for( y = o_y/VIS_ZOOM+1; y < v_h; y++ )
307         {
308             /* left line */
309             p_oyp->p_pixels[
310                 y*p_oyp->i_pitch+o_x/VIS_ZOOM
311             ] = 0xff;
312             /* right line */
313             p_oyp->p_pixels[
314                 y*p_oyp->i_pitch+o_x/VIS_ZOOM + v_w
315             ] = 0xff;
316         }
317         /* bottom line */
318         p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
319                                      + v_h*p_oyp->i_pitch
320                                      + o_x/VIS_ZOOM, 0xff, v_w+1 );
321
322         /* */
323         v_h = p_oyp->i_lines/VIS_ZOOM;
324     }
325     else
326     {
327         v_h = 1;
328     }
329
330     /* print a small "VLC ZOOM" ... gruikkkkkkkkk */
331 #define DRAW(a) {int c,l=1;L a;}
332 #define L ;l++,c=1
333 #define X ;draw(l,c);c+=1
334 #define o +1
335 #define draw(y,x) p_oyp->p_pixels[(v_h+y)*p_oyp->i_pitch+x] = 0xff;
336 if( p_vout->p_sys->b_visible )
337 DRAW(
338 X o o o X o X o o o o o o X X X X o o o X X X X X o o X X X o o o X X X o o X X o X X o o o X o o o X o X X X X X o X X X X o o X X X X X L
339 X o o o X o X o o o o o X o o o o o o o o o o X o o X o o o X o X o o o X o X o X o X o o o X o o o X o o o X o o o X o o o X o X o o o o L
340 o X o X o o X o o o o o X o o o o o o o o o X o o o X o o o X o X o o o X o X o o o X o o o X X X X X o o o X o o o X o o o X o X X X X o L
341 o X o X o o X o o o o o X o o o o o o o o X o o o o X o o o X o X o o o X o X o o o X o o o X o o o X o o o X o o o X o o o X o X o o o o L
342 o o X o o o X X X X X o o X X X X o o o X X X X X o o X X X o o o X X X o o X o o o X o o o X o o o X o X X X X X o X X X X o o X X X X X L
343 )
344 else
345 DRAW(
346 X o o o X o X o o o o o o X X X X o o o X X X X X o o X X X o o o X X X o o X X o X X o o o o X X X X o X o o o X o o X X X o o X o o o X L
347 X o o o X o X o o o o o X o o o o o o o o o o X o o X o o o X o X o o o X o X o X o X o o o X o o o o o X o o o X o X o o o X o X o o o X L
348 o X o X o o X o o o o o X o o o o o o o o o X o o o X o o o X o X o o o X o X o o o X o o o o X X X o o X X X X X o X o o o X o X o X o X L
349 o X o X o o X o o o o o X o o o o o o o o X o o o o X o o o X o X o o o X o X o o o X o o o o o o o X o X o o o X o X o o o X o X o X o X L
350 o o X o o o X X X X X o o X X X X o o o X X X X X o o X X X o o o X X X o o X o o o X o o o X X X X o o X o o o X o o X X X o o o X o X o L
351 )
352 #undef DRAW
353 #undef L
354 #undef X
355 #undef O
356 #undef draw
357
358     if( p_vout->p_sys->b_visible )
359     {
360         /* zoom gauge */
361         p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
362                                      + (v_h+9)*p_oyp->i_pitch,
363                                      0xff, 41 );
364         for( y = v_h + 10; y < v_h + 90; y++ )
365         {
366             int width = v_h + 90 - y;
367             width = (width*width)/160;
368             if( (80 - y + v_h)*10 < o_zoom )
369             {
370                 p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
371                                              + y*p_oyp->i_pitch,
372                                              0xff, width );
373             }
374             else
375             {
376                 p_oyp->p_pixels[y*p_oyp->i_pitch] = 0xff;
377                 p_oyp->p_pixels[y*p_oyp->i_pitch + width - 1] = 0xff;
378             }
379         }
380     }
381
382     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
383 }
384
385 /*****************************************************************************
386  * SendEvents: forward mouse and keyboard events to the parent p_vout
387  *****************************************************************************/
388 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
389                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
390 {
391     var_Set( (vlc_object_t *)p_data, psz_var, newval );
392
393     return VLC_SUCCESS;
394 }
395
396 /*****************************************************************************
397  * SendEventsToChild: forward events to the child/children vout
398  *****************************************************************************/
399 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
400                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
401 {
402     vout_thread_t *p_vout = (vout_thread_t *)p_this;
403     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
404     return VLC_SUCCESS;
405 }
406
407 /*****************************************************************************
408  * MouseEvent: callback for mouse events
409  *****************************************************************************/
410 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
411                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
412 {
413     vout_thread_t *p_vout = (vout_thread_t*)p_data;
414     vlc_value_t vald,valx,valy;
415
416 #define MOUSE_DOWN    1
417 #define MOUSE_CLICKED 2
418 #define MOUSE_MOVE_X  4
419 #define MOUSE_MOVE_Y  8
420 #define MOUSE_MOVE    12
421     uint8_t mouse= 0;
422
423     int v_h = p_vout->output.i_height*ZOOM_FACTOR/p_vout->p_sys->i_zoom;
424     int v_w = p_vout->output.i_width*ZOOM_FACTOR/p_vout->p_sys->i_zoom;
425
426     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
427     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
428     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
429
430     var_Get( p_vout->p_sys->p_vout, "mouse-button-down", &vald );
431     if( vald.i_int & 0x1 ) mouse |= MOUSE_DOWN;
432     var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
433     var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
434
435     if( ( mouse&MOUSE_MOVE && mouse&MOUSE_DOWN)
436         || mouse&MOUSE_CLICKED )
437     {
438     /* (mouse moved and mouse button is down) or (mouse clicked) */
439         if( p_vout->p_sys->b_visible )
440         {
441             if(    0 <= valy.i_int
442                 && valy.i_int < (int)p_vout->output.i_height/VIS_ZOOM
443                 && 0 <= valx.i_int
444                 && valx.i_int < (int)p_vout->output.i_width/VIS_ZOOM )
445             {
446             /* mouse is over visualisation */
447                 p_vout->p_sys->i_x = __MIN( __MAX( valx.i_int*VIS_ZOOM - v_w/2, 0 ),
448                                             p_vout->output.i_width - v_w - 1);
449                 p_vout->p_sys->i_y = __MIN( __MAX( valy.i_int * VIS_ZOOM - v_h/2,
450                                         0 ), p_vout->output.i_height - v_h - 1);
451             }
452             else if( valx.i_int >= 0 && valx.i_int < 80
453                 && valy.i_int >= (int)p_vout->output.i_height/VIS_ZOOM
454                 && valy.i_int < (int)p_vout->output.i_height/VIS_ZOOM + 9
455                 && mouse&MOUSE_CLICKED )
456             {
457             /* mouse is over the "VLC ZOOM HIDE" text */
458                 p_vout->p_sys->b_visible = VLC_FALSE;
459             }
460             else if(    (int)p_vout->output.i_height/VIS_ZOOM + 9 <= valy.i_int
461                      && valy.i_int <= (int)p_vout->output.i_height/VIS_ZOOM + 90
462                      && 0 <= valx.i_int
463                      && valx.i_int <=
464                      (( (int)p_vout->output.i_height/VIS_ZOOM + 90 -  valy.i_int)
465                *( (int)p_vout->output.i_height/VIS_ZOOM + 90 -  valy.i_int))/160 )
466             {
467             /* mouse is over zoom gauge */
468                 p_vout->p_sys->i_zoom = __MAX( ZOOM_FACTOR,
469                                 ( 80 + (int)p_vout->output.i_height/VIS_ZOOM
470                                    - valy.i_int + 2) * ZOOM_FACTOR/10 );
471             }
472             else if( mouse&MOUSE_MOVE_X && !(mouse&MOUSE_CLICKED) )
473             {
474                 p_vout->p_sys->i_x -= (newval.i_int - oldval.i_int)
475                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
476             }
477             else if( mouse&MOUSE_MOVE_Y && !(mouse&MOUSE_CLICKED) )
478             {
479                 p_vout->p_sys->i_y -= (newval.i_int - oldval.i_int)
480                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
481             }
482         }
483         else
484         {
485             if( valx.i_int >= 0 && valx.i_int < 80 && valy.i_int >= 0
486                 && valy.i_int <= 10 && mouse&MOUSE_CLICKED )
487             {
488             /* mouse is over the "VLC ZOOM SHOW" text */
489                 p_vout->p_sys->b_visible = VLC_TRUE;
490             }
491             else if( mouse&MOUSE_MOVE_X && !(mouse&MOUSE_CLICKED) )
492             {
493                 p_vout->p_sys->i_x -= (newval.i_int - oldval.i_int)
494                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
495             }
496             else if( mouse&MOUSE_MOVE_Y && !(mouse&MOUSE_CLICKED) )
497             {
498                 p_vout->p_sys->i_y -= (newval.i_int - oldval.i_int)
499                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
500             }
501         }
502     }
503
504     p_vout->p_sys->i_x =
505          __MAX( 0, __MIN( p_vout->p_sys->i_x, (int)p_vout->output.i_width
506          - (int)p_vout->output.i_width*ZOOM_FACTOR/p_vout->p_sys->i_zoom - 1 ));
507     p_vout->p_sys->i_y =
508          __MAX( 0, __MIN( p_vout->p_sys->i_y, (int)p_vout->output.i_height
509         - (int)p_vout->output.i_height*ZOOM_FACTOR/p_vout->p_sys->i_zoom - 1 ));
510
511
512     return VLC_SUCCESS;
513 }