1 /*****************************************************************************
2 * magnify.c : Magnify/Zoom interactive effect
3 *****************************************************************************
4 * Copyright (C) 2005-2009 VLC authors and VideoLAN
6 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_image.h>
36 #include <vlc_filter.h>
37 #include <vlc_mouse.h>
38 #include <vlc_picture.h>
39 #include "filter_picture.h"
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Create ( vlc_object_t
* );
45 static void Destroy ( vlc_object_t
* );
48 set_description( N_("Magnify/Zoom interactive video filter") )
49 set_shortname( N_( "Magnify" ))
50 set_capability( "video filter", 0 )
51 set_category( CAT_VIDEO
)
52 set_subcategory( SUBCAT_VIDEO_VFILTER
)
54 set_callbacks( Create
, Destroy
)
58 /*****************************************************************************
60 *****************************************************************************/
61 static picture_t
*Filter( filter_t
*, picture_t
* );
62 static int Mouse( filter_t
*, vlc_mouse_t
*, const vlc_mouse_t
* );
65 static void DrawZoomStatus( uint8_t *, int i_pitch
, int i_width
, int i_height
,
66 int i_offset_x
, int i_offset_y
, bool b_visible
);
67 static void DrawRectangle( uint8_t *, int i_pitch
, int i_width
, int i_height
,
68 int x
, int y
, int i_w
, int i_h
);
73 image_handler_t
*p_image
;
75 vlc_tick_t i_hide_timeout
;
77 int i_zoom
; /* zoom level in percent */
78 int i_x
, i_y
; /* top left corner coordinates in original image */
80 bool b_visible
; /* is "interface" visible ? */
82 vlc_tick_t i_last_activity
;
88 /*****************************************************************************
90 *****************************************************************************/
91 static int Create( vlc_object_t
*p_this
)
93 filter_t
*p_filter
= (filter_t
*)p_this
;
97 switch( p_filter
->fmt_in
.i_codec
)
103 msg_Err( p_filter
, "Unsupported chroma %4.4s", (char *)&p_filter
->fmt_in
.i_codec
);
106 if( !es_format_IsSimilar( &p_filter
->fmt_in
, &p_filter
->fmt_out
) )
108 msg_Err( p_filter
, "Input and output format does not match" );
112 /* Allocate structure */
113 p_filter
->p_sys
= p_sys
= malloc( sizeof( *p_sys
) );
114 if( !p_filter
->p_sys
)
117 p_sys
->p_image
= image_HandlerCreate( p_filter
);
118 if( !p_sys
->p_image
)
126 p_sys
->i_zoom
= 2*ZOOM_FACTOR
;
127 p_sys
->b_visible
= true;
128 p_sys
->i_last_activity
= vlc_tick_now();
129 p_sys
->i_hide_timeout
= VLC_TICK_FROM_MS( var_InheritInteger( p_filter
, "mouse-hide-timeout" ) );
132 p_filter
->pf_video_filter
= Filter
;
133 p_filter
->pf_video_mouse
= Mouse
;
137 /*****************************************************************************
139 *****************************************************************************/
140 static void Destroy( vlc_object_t
*p_this
)
142 filter_t
*p_filter
= (filter_t
*)p_this
;
143 filter_sys_t
*p_sys
= p_filter
->p_sys
;
145 image_HandlerDelete( p_sys
->p_image
);
150 /*****************************************************************************
151 * Render: displays previously rendered output
152 *****************************************************************************/
153 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
155 filter_sys_t
*p_sys
= p_filter
->p_sys
;
159 picture_t
*p_converted
;
162 p_outpic
= filter_NewPicture( p_filter
);
165 picture_Release( p_pic
);
170 const bool b_visible
= p_sys
->b_visible
;
171 const int o_x
= p_sys
->i_x
;
172 const int o_y
= p_sys
->i_y
;
173 const int o_zoom
= p_sys
->i_zoom
;
175 /* background magnified image */
176 if( o_zoom
!= ZOOM_FACTOR
)
178 video_format_t fmt_in
;
179 video_format_t fmt_out
;
180 plane_t orig_planes
[PICTURE_PLANE_MAX
];
181 memcpy(orig_planes
, p_pic
->p
, sizeof orig_planes
);
183 for( int i_plane
= 0; i_plane
< p_pic
->i_planes
; i_plane
++ )
185 const int o_yp
= o_y
* p_outpic
->p
[i_plane
].i_visible_lines
/ p_outpic
->p
[Y_PLANE
].i_visible_lines
;
186 const int o_xp
= o_x
* p_outpic
->p
[i_plane
].i_visible_pitch
/ p_outpic
->p
[Y_PLANE
].i_visible_pitch
;
188 p_pic
->p
[i_plane
].p_pixels
+= o_yp
* p_pic
->p
[i_plane
].i_pitch
+ o_xp
;
192 fmt_in
= p_filter
->fmt_in
.video
;
193 fmt_in
.i_width
= fmt_in
.i_visible_width
= (fmt_in
.i_visible_width
* ZOOM_FACTOR
/ o_zoom
) & ~1;
194 fmt_in
.i_height
= fmt_in
.i_visible_height
= (fmt_in
.i_visible_height
* ZOOM_FACTOR
/ o_zoom
) & ~1;
197 fmt_out
= p_filter
->fmt_out
.video
;
198 p_converted
= image_Convert( p_sys
->p_image
, p_pic
, &fmt_in
, &fmt_out
);
199 memcpy(p_pic
->p
, orig_planes
, sizeof orig_planes
);
201 picture_CopyPixels( p_outpic
, p_converted
);
203 picture_Release( p_converted
);
207 picture_CopyPixels( p_outpic
, p_pic
);
210 /* zoom area selector */
211 p_oyp
= &p_outpic
->p
[Y_PLANE
];
214 video_format_t fmt_out
;
216 /* image visualization */
217 fmt_out
= p_filter
->fmt_out
.video
;
218 fmt_out
.i_width
= fmt_out
.i_visible_width
= (fmt_out
.i_visible_width
/VIS_ZOOM
) & ~1;
219 fmt_out
.i_height
= fmt_out
.i_visible_height
= (fmt_out
.i_visible_height
/VIS_ZOOM
) & ~1;
220 p_converted
= image_Convert( p_sys
->p_image
, p_pic
,
221 &p_pic
->format
, &fmt_out
);
223 /* It will put only what can be copied at the top left */
224 picture_CopyPixels( p_outpic
, p_converted
);
226 picture_Release( p_converted
);
228 /* white rectangle on visualization */
229 v_w
= __MIN( fmt_out
.i_visible_width
* ZOOM_FACTOR
/ o_zoom
, fmt_out
.i_visible_width
- 1 );
230 v_h
= __MIN( fmt_out
.i_visible_height
* ZOOM_FACTOR
/ o_zoom
, fmt_out
.i_visible_height
- 1 );
232 DrawRectangle( p_oyp
->p_pixels
, p_oyp
->i_pitch
,
233 p_oyp
->i_visible_pitch
, p_oyp
->i_visible_lines
,
234 o_x
/VIS_ZOOM
, o_y
/VIS_ZOOM
,
238 v_h
= fmt_out
.i_visible_height
+ 1;
245 /* print a small hide/show toggle text control */
247 if( b_visible
|| p_sys
->i_last_activity
+ p_sys
->i_hide_timeout
> vlc_tick_now() )
248 DrawZoomStatus( p_oyp
->p_pixels
, p_oyp
->i_pitch
, p_oyp
->i_visible_pitch
, p_oyp
->i_lines
,
254 memset( p_oyp
->p_pixels
+ (v_h
+9)*p_oyp
->i_pitch
, 0xff, 41 );
255 for( int y
= v_h
+ 10; y
< v_h
+ 90; y
++ )
257 int i_width
= v_h
+ 90 - y
;
258 i_width
= i_width
* i_width
/ 160;
259 if( (80 - y
+ v_h
)*ZOOM_FACTOR
/10 < o_zoom
)
261 memset( p_oyp
->p_pixels
+ y
*p_oyp
->i_pitch
, 0xff, i_width
);
265 p_oyp
->p_pixels
[y
*p_oyp
->i_pitch
] = 0xff;
266 p_oyp
->p_pixels
[y
*p_oyp
->i_pitch
+ i_width
- 1] = 0xff;
271 return CopyInfoAndRelease( p_outpic
, p_pic
);
274 static void DrawZoomStatus( uint8_t *pb_dst
, int i_pitch
, int i_width
, int i_height
,
275 int i_offset_x
, int i_offset_y
, bool b_visible
)
277 static const char *p_hide
=
278 "X X XXXXX XXXX XXXXX XXXXX XXX XXX XX XXL"
279 "X X X X X X X X X X X X X XL"
280 "XXXXX X X X XXXX X X X X X X XL"
281 "X X X X X X X X X X X X XL"
282 "X X XXXXX XXXX XXXXX XXXXX XXX XXX X XL";
283 static const char *p_show
=
284 " XXXX X X XXX X X XXXXX XXX XXX XX XXL"
285 "X X X X X X X X X X X X X X XL"
286 " XXX XXXXX X X X X X X X X X X X XL"
287 " X X X X X X X X X X X X X X XL"
288 "XXXX X X XXX X X XXXXX XXX XXX X XL";
289 const char *p_draw
= b_visible
? p_hide
: p_show
;
291 for( int i
= 0, x
= i_offset_x
, y
= i_offset_y
; p_draw
[i
] != '\0'; i
++ )
293 if( p_draw
[i
] == 'X' )
295 if( x
< i_width
&& y
< i_height
)
296 pb_dst
[y
*i_pitch
+ x
] = 0xff;
299 else if( p_draw
[i
] == ' ' )
303 else if( p_draw
[i
] == 'L' )
310 static void DrawRectangle( uint8_t *pb_dst
, int i_pitch
, int i_width
, int i_height
,
311 int x
, int y
, int i_w
, int i_h
)
313 if( x
+ i_w
> i_width
|| y
+ i_h
> i_height
)
317 memset( &pb_dst
[y
* i_pitch
+ x
], 0xff, i_w
);
320 for( int dy
= 1; dy
< i_h
-1; dy
++ )
322 pb_dst
[(y
+dy
) * i_pitch
+ x
+ 0] = 0xff;
323 pb_dst
[(y
+dy
) * i_pitch
+ x
+ i_w
-1] = 0xff;
327 memset( &pb_dst
[(y
+i_h
-1) * i_pitch
+ x
], 0xff, i_w
);
330 static int Mouse( filter_t
*p_filter
, vlc_mouse_t
*p_new
, const vlc_mouse_t
*p_old
)
332 filter_sys_t
*p_sys
= p_filter
->p_sys
;
333 const video_format_t
*p_fmt
= &p_filter
->fmt_in
.video
;
336 const bool b_click
= vlc_mouse_HasPressed( p_old
, p_new
, MOUSE_BUTTON_LEFT
);
337 const bool b_pressed
= vlc_mouse_IsLeftPressed( p_new
);
341 /* Find the mouse position */
342 if( p_sys
->b_visible
)
344 const int i_visu_width
= p_fmt
->i_visible_width
/ VIS_ZOOM
;
345 const int i_visu_height
= p_fmt
->i_visible_height
/ VIS_ZOOM
;
347 if( p_new
->i_x
>= 0 && p_new
->i_x
< i_visu_width
&&
348 p_new
->i_y
>= 0 && p_new
->i_y
< i_visu_height
)
353 const int v_w
= p_fmt
->i_visible_width
* ZOOM_FACTOR
/ p_sys
->i_zoom
;
354 const int v_h
= p_fmt
->i_visible_height
* ZOOM_FACTOR
/ p_sys
->i_zoom
;
356 p_sys
->i_x
= VLC_CLIP( p_new
->i_x
* VIS_ZOOM
- v_w
/2, 0,
357 (int)p_fmt
->i_visible_width
- v_w
- 1);
358 p_sys
->i_y
= VLC_CLIP( p_new
->i_y
* VIS_ZOOM
- v_h
/2, 0,
359 (int)p_fmt
->i_visible_height
- v_h
- 1);
364 else if( p_new
->i_x
>= 0 && p_new
->i_x
< 80 &&
365 p_new
->i_y
>= i_visu_height
&&
366 p_new
->i_y
< i_visu_height
+ 9 )
371 p_sys
->b_visible
= false;
375 else if( p_new
->i_x
>= 0 &&
376 p_new
->i_x
<= ( i_visu_height
+ 90 - p_new
->i_y
) *
377 ( i_visu_height
+ 90 - p_new
->i_y
) / 160 &&
378 p_new
->i_y
>= i_visu_height
+ 9 &&
379 p_new
->i_y
<= i_visu_height
+ 90 )
384 p_sys
->i_zoom
= __MAX( ZOOM_FACTOR
,
385 (80 + i_visu_height
- p_new
->i_y
+ 2) *
388 const int v_w
= p_fmt
->i_visible_width
* ZOOM_FACTOR
/ p_sys
->i_zoom
;
389 const int v_h
= p_fmt
->i_visible_height
* ZOOM_FACTOR
/ p_sys
->i_zoom
;
390 p_sys
->i_x
= VLC_CLIP( p_sys
->i_x
, 0, (int)p_fmt
->i_visible_width
- v_w
- 1 );
391 p_sys
->i_y
= VLC_CLIP( p_sys
->i_y
, 0, (int)p_fmt
->i_visible_height
- v_h
- 1 );
399 if( p_new
->i_x
>= 0 && p_new
->i_x
< 80 &&
400 p_new
->i_y
>= 0 && p_new
->i_y
<= 10 )
405 p_sys
->b_visible
= true;
411 if( vlc_mouse_HasMoved( p_old
, p_new
) )
412 p_sys
->i_last_activity
= vlc_tick_now();
418 p_new
->i_x
= p_sys
->i_x
+ p_new
->i_x
* ZOOM_FACTOR
/ p_sys
->i_zoom
;
419 p_new
->i_y
= p_sys
->i_y
+ p_new
->i_y
* ZOOM_FACTOR
/ p_sys
->i_zoom
;