]> git.sesse.net Git - vlc/blob - modules/video_filter/magnify.c
use pf_mem{set,cpy} when possible
[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 = {0};
126
127     I_OUTPUTPICTURES = 0;
128
129     /* Initialize the output structure */
130     p_vout->output.i_chroma = p_vout->render.i_chroma;
131     p_vout->output.i_width  = p_vout->render.i_width;
132     p_vout->output.i_height = p_vout->render.i_height;
133     p_vout->output.i_aspect = p_vout->render.i_aspect;
134
135     p_vout->fmt_out = p_vout->fmt_in;
136     fmt = p_vout->fmt_out;
137
138     /* Try to open the real video output */
139     msg_Dbg( p_vout, "spawning the real video output" );
140
141     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
142
143     /* Everything failed */
144     if( p_vout->p_sys->p_vout == NULL )
145     {
146         msg_Err( p_vout, "cannot open vout, aborting" );
147         return VLC_EGENERIC;
148     }
149
150 #define VIS_ZOOM 4
151     p_vout->p_sys->i_x = 0;
152     p_vout->p_sys->i_y = 0;
153 #define ZOOM_FACTOR 100
154     p_vout->p_sys->i_zoom = 200;
155
156     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
157     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
158     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
159                      MouseEvent, p_vout);
160
161     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
162     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
163     ADD_PARENT_CALLBACKS( SendEventsToChild );
164
165     return VLC_SUCCESS;
166 }
167
168 /*****************************************************************************
169  * End: terminate Magnify video thread output method
170  *****************************************************************************/
171 static void End( vout_thread_t *p_vout )
172 {
173     int i_index;
174
175     /* Free the fake output buffers we allocated */
176     for( i_index = I_OUTPUTPICTURES ; i_index ; )
177     {
178         i_index--;
179         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
180     }
181
182     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
183     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
184     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
185 }
186
187 /*****************************************************************************
188  * Destroy: destroy Magnify video thread output method
189  *****************************************************************************/
190 static void Destroy( vlc_object_t *p_this )
191 {
192     vout_thread_t *p_vout = (vout_thread_t *)p_this;
193
194     if( p_vout->p_sys->p_vout )
195     {
196         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
197         vlc_object_detach( p_vout->p_sys->p_vout );
198         vout_Destroy( p_vout->p_sys->p_vout );
199     }
200
201     image_HandlerDelete( p_vout->p_sys->p_image );
202
203     DEL_PARENT_CALLBACKS( SendEventsToChild );
204
205     free( p_vout->p_sys );
206 }
207
208 /*****************************************************************************
209  * Render: displays previously rendered output
210  *****************************************************************************/
211 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
212 {
213     picture_t *p_outpic;
214
215     int o_x = p_vout->p_sys->i_x;
216     int o_y = p_vout->p_sys->i_y;
217     int o_zoom = p_vout->p_sys->i_zoom;
218     int x,y,o_yp,o_xp;
219     int v_w, v_h;
220     video_format_t fmt_out = {0};
221     picture_t *p_converted;
222     plane_t *p_oyp=NULL;
223
224     /* This is a new frame. Get a structure from the video_output. */
225     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
226               == NULL )
227     {
228         if( p_vout->b_die || p_vout->b_error )
229         {
230             return;
231         }
232         msleep( VOUT_OUTMEM_SLEEP );
233     }
234
235     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
236
237     p_oyp = &(p_outpic->p[Y_PLANE]);
238
239     /* background magnified image */
240     if( o_zoom != ZOOM_FACTOR )
241     {
242 #define magnify( plane ) \
243     o_yp = o_y*p_outpic->p[plane].i_lines/p_outpic->p[Y_PLANE].i_lines; \
244     o_xp = o_x*p_outpic->p[plane].i_pitch/p_outpic->p[Y_PLANE].i_pitch; \
245     for( y=0; y<p_outpic->p[plane].i_visible_lines; y++ ) \
246     { \
247         for( x=0; x<p_outpic->p[plane].i_visible_pitch; x++ ) \
248         { \
249             p_outpic->p[plane].p_pixels[y*p_outpic->p[plane].i_pitch+x] = \
250                 p_pic->p[plane].p_pixels[ \
251                     ( o_yp + y*ZOOM_FACTOR/o_zoom )*p_outpic->p[plane].i_pitch \
252                     + o_xp + x*ZOOM_FACTOR/o_zoom \
253                 ]; \
254         } \
255     }
256     magnify( Y_PLANE );
257     magnify( U_PLANE );
258     magnify( V_PLANE );
259 #undef magnify
260     }
261     else
262     {
263 #define copy( plane ) \
264         p_vout->p_libvlc-> \
265         pf_memcpy( p_outpic->p[plane].p_pixels, p_pic->p[plane].p_pixels, \
266             p_outpic->p[plane].i_lines * p_outpic->p[plane].i_pitch );
267         copy( Y_PLANE );
268         copy( U_PLANE );
269         copy( V_PLANE );
270 #undef copy
271     }
272
273     if( p_vout->p_sys->b_visible )
274     {
275         /* image visualization */
276         fmt_out = p_vout->fmt_out;
277         fmt_out.i_width = p_vout->render.i_width/VIS_ZOOM;
278         fmt_out.i_height = p_vout->render.i_height/VIS_ZOOM;
279         p_converted = image_Convert( p_vout->p_sys->p_image, p_pic,
280                                      &(p_pic->format), &fmt_out );
281     #define copyimage( plane ) \
282         for( y=0; y<p_converted->p[plane].i_visible_lines; y++) \
283         { \
284             p_vout->p_libvlc->pf_memcpy( \
285             p_outpic->p[plane].p_pixels+y*p_outpic->p[plane].i_pitch, \
286             p_converted->p[plane].p_pixels+y*p_converted->p[plane].i_pitch, \
287             p_converted->p[plane].i_visible_pitch ); \
288         }
289         copyimage( Y_PLANE );
290         copyimage( U_PLANE );
291         copyimage( V_PLANE );
292     #undef copyimage
293         p_converted->pf_release( p_converted );
294
295         /* white rectangle on visualization */
296         v_w = p_oyp->i_pitch*ZOOM_FACTOR/(VIS_ZOOM*o_zoom);
297         v_h = (o_y+p_oyp->i_lines*ZOOM_FACTOR/o_zoom)/VIS_ZOOM;
298         /* top line */
299         p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
300                                      + o_y/VIS_ZOOM*p_oyp->i_pitch
301                                      + o_x/VIS_ZOOM, 0xff, v_w+1 );
302
303         for( y = o_y/VIS_ZOOM+1; y < v_h; y++ )
304         {
305             /* left line */
306             p_oyp->p_pixels[
307                 y*p_oyp->i_pitch+o_x/VIS_ZOOM
308             ] = 0xff;
309             /* right line */
310             p_oyp->p_pixels[
311                 y*p_oyp->i_pitch+o_x/VIS_ZOOM + v_w
312             ] = 0xff;
313         }
314         /* bottom line */
315         p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
316                                      + v_h*p_oyp->i_pitch
317                                      + o_x/VIS_ZOOM, 0xff, v_w+1 );
318
319         /* */
320         v_h = p_oyp->i_lines/VIS_ZOOM;
321     }
322     else
323     {
324         v_h = 1;
325     }
326
327     /* print a small "VLC ZOOM" ... gruikkkkkkkkk */
328 #define DRAW(a) {int c,l=1;L a;}
329 #define L ;l++,c=1
330 #define X ;draw(l,c);c+=1
331 #define o +1
332 #define draw(y,x) p_oyp->p_pixels[(v_h+y)*p_oyp->i_pitch+x] = 0xff;
333 if( p_vout->p_sys->b_visible )
334 DRAW(
335 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
336 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
337 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
338 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
339 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
340 )
341 else
342 DRAW(
343 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
344 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
345 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
346 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
347 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
348 )
349 #undef DRAW
350 #undef L
351 #undef X
352 #undef O
353 #undef draw
354
355     if( p_vout->p_sys->b_visible )
356     {
357         /* zoom gauge */
358         p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
359                                      + (v_h+9)*p_oyp->i_pitch,
360                                      0xff, 41 );
361         for( y = v_h + 10; y < v_h + 90; y++ )
362         {
363             int width = v_h + 90 - y;
364             width = (width*width)/160;
365             if( (80 - y + v_h)*10 < o_zoom )
366             {
367                 p_vout->p_libvlc->pf_memset( p_oyp->p_pixels
368                                              + y*p_oyp->i_pitch,
369                                              0xff, width );
370             }
371             else
372             {
373                 p_oyp->p_pixels[y*p_oyp->i_pitch] = 0xff;
374                 p_oyp->p_pixels[y*p_oyp->i_pitch + width - 1] = 0xff;
375             }
376         }
377     }
378
379     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
380 }
381
382 /*****************************************************************************
383  * SendEvents: forward mouse and keyboard events to the parent p_vout
384  *****************************************************************************/
385 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
386                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
387 {
388     var_Set( (vlc_object_t *)p_data, psz_var, newval );
389
390     return VLC_SUCCESS;
391 }
392
393 /*****************************************************************************
394  * SendEventsToChild: forward events to the child/children vout
395  *****************************************************************************/
396 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
397                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
398 {
399     vout_thread_t *p_vout = (vout_thread_t *)p_this;
400     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
401     return VLC_SUCCESS;
402 }
403
404 /*****************************************************************************
405  * MouseEvent: callback for mouse events
406  *****************************************************************************/
407 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
408                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
409 {
410     vout_thread_t *p_vout = (vout_thread_t*)p_data;
411     vlc_value_t vald,valx,valy;
412
413 #define MOUSE_DOWN    1
414 #define MOUSE_CLICKED 2
415 #define MOUSE_MOVE_X  4
416 #define MOUSE_MOVE_Y  8
417 #define MOUSE_MOVE    12
418     uint8_t mouse= 0;
419
420     int v_h = p_vout->output.i_height*ZOOM_FACTOR/p_vout->p_sys->i_zoom;
421     int v_w = p_vout->output.i_width*ZOOM_FACTOR/p_vout->p_sys->i_zoom;
422
423     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
424     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
425     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
426
427     var_Get( p_vout->p_sys->p_vout, "mouse-button-down", &vald );
428     if( vald.i_int & 0x1 ) mouse |= MOUSE_DOWN;
429     var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
430     var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
431
432     if( ( mouse&MOUSE_MOVE && mouse&MOUSE_DOWN)
433         || mouse&MOUSE_CLICKED )
434     {
435     /* (mouse moved and mouse button is down) or (mouse clicked) */
436         if( p_vout->p_sys->b_visible )
437         {
438             if(    0 <= valy.i_int
439                 && valy.i_int < (int)p_vout->output.i_height/VIS_ZOOM
440                 && 0 <= valx.i_int
441                 && valx.i_int < (int)p_vout->output.i_width/VIS_ZOOM )
442             {
443             /* mouse is over visualisation */
444                 p_vout->p_sys->i_x = __MIN( __MAX( valx.i_int*VIS_ZOOM - v_w/2, 0 ),
445                                             p_vout->output.i_width - v_w - 1);
446                 p_vout->p_sys->i_y = __MIN( __MAX( valy.i_int * VIS_ZOOM - v_h/2,
447                                         0 ), p_vout->output.i_height - v_h - 1);
448             }
449             else if( valx.i_int >= 0 && valx.i_int < 80
450                 && valy.i_int >= (int)p_vout->output.i_height/VIS_ZOOM
451                 && valy.i_int < (int)p_vout->output.i_height/VIS_ZOOM + 9
452                 && mouse&MOUSE_CLICKED )
453             {
454             /* mouse is over the "VLC ZOOM HIDE" text */
455                 p_vout->p_sys->b_visible = VLC_FALSE;
456             }
457             else if(    (int)p_vout->output.i_height/VIS_ZOOM + 9 <= valy.i_int
458                      && valy.i_int <= (int)p_vout->output.i_height/VIS_ZOOM + 90
459                      && 0 <= valx.i_int
460                      && valx.i_int <=
461                      (( (int)p_vout->output.i_height/VIS_ZOOM + 90 -  valy.i_int)
462                *( (int)p_vout->output.i_height/VIS_ZOOM + 90 -  valy.i_int))/160 )
463             {
464             /* mouse is over zoom gauge */
465                 p_vout->p_sys->i_zoom = __MAX( ZOOM_FACTOR,
466                                 ( 80 + (int)p_vout->output.i_height/VIS_ZOOM
467                                    - valy.i_int + 2) * ZOOM_FACTOR/10 );
468             }
469             else if( mouse&MOUSE_MOVE_X && !(mouse&MOUSE_CLICKED) )
470             {
471                 p_vout->p_sys->i_x -= (newval.i_int - oldval.i_int)
472                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
473             }
474             else if( mouse&MOUSE_MOVE_Y && !(mouse&MOUSE_CLICKED) )
475             {
476                 p_vout->p_sys->i_y -= (newval.i_int - oldval.i_int)
477                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
478             }
479         }
480         else
481         {
482             if( valx.i_int >= 0 && valx.i_int < 80 && valy.i_int >= 0
483                 && valy.i_int <= 10 && mouse&MOUSE_CLICKED )
484             {
485             /* mouse is over the "VLC ZOOM SHOW" text */
486                 p_vout->p_sys->b_visible = VLC_TRUE;
487             }
488             else if( mouse&MOUSE_MOVE_X && !(mouse&MOUSE_CLICKED) )
489             {
490                 p_vout->p_sys->i_x -= (newval.i_int - oldval.i_int)
491                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
492             }
493             else if( mouse&MOUSE_MOVE_Y && !(mouse&MOUSE_CLICKED) )
494             {
495                 p_vout->p_sys->i_y -= (newval.i_int - oldval.i_int)
496                                       *ZOOM_FACTOR/p_vout->p_sys->i_zoom;
497             }
498         }
499     }
500
501     p_vout->p_sys->i_x =
502          __MAX( 0, __MIN( p_vout->p_sys->i_x, (int)p_vout->output.i_width
503          - (int)p_vout->output.i_width*ZOOM_FACTOR/p_vout->p_sys->i_zoom - 1 ));
504     p_vout->p_sys->i_y =
505          __MAX( 0, __MIN( p_vout->p_sys->i_y, (int)p_vout->output.i_height
506         - (int)p_vout->output.i_height*ZOOM_FACTOR/p_vout->p_sys->i_zoom - 1 ));
507
508
509     return VLC_SUCCESS;
510 }