A little more detail regarding using my github copies of the code with where it's...
[vlc/adversarial.git] / modules / video_filter / freeze.c
blob015346d3b597ae14a8c0ef2d66cf987e1bdb8f8d
1 /*****************************************************************************
2 * freeze.c : Freezing video filter
3 *****************************************************************************
4 * Copyright (C) 2013 Vianney Boyer
5 * $Id$
7 * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
36 #include "filter_picture.h"
38 #ifndef MOD
39 # define MOD(a, b) ((((a)%(b)) + (b))%(b))
40 #endif
42 struct filter_sys_t {
43 bool b_init;
45 int32_t i_planes;
46 int32_t *i_height;
47 int32_t *i_width;
48 int32_t *i_visible_pitch;
49 int8_t ***pi_freezed_picture; /* records freezed pixels */
50 int16_t **pi_freezing_countdown; /* freezed pixel delay */
51 bool **pb_update_cache; /* update chache request */
55 /*****************************************************************************
56 * Prototypes
57 *****************************************************************************/
59 static picture_t *Filter( filter_t *, picture_t * );
61 static int freeze_mouse( filter_t *, vlc_mouse_t *,
62 const vlc_mouse_t *, const vlc_mouse_t * );
63 static int freeze_allocate_data( filter_t *, picture_t * );
64 static void freeze_free_allocated_data( filter_t * );
67 /*****************************************************************************
68 * Module descriptor
69 *****************************************************************************/
71 #define CFG_PREFIX "freeze-"
73 static int Open ( vlc_object_t * );
74 static void Close( vlc_object_t * );
76 vlc_module_begin()
77 set_description( N_("Freezing interactive video filter") )
78 set_shortname( N_("Freeze" ) )
79 set_capability( "video filter2", 0 )
80 set_category( CAT_VIDEO )
81 set_subcategory( SUBCAT_VIDEO_VFILTER )
83 set_callbacks( Open, Close )
84 vlc_module_end()
86 /*****************************************************************************
87 * Local prototypes
88 *****************************************************************************/
90 /**
91 * Open the filter
93 static int Open( vlc_object_t *p_this )
95 filter_t *p_filter = (filter_t *)p_this;
96 filter_sys_t *p_sys;
98 /* Assert video in match with video out */
99 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
100 msg_Err( p_filter, "Input and output format does not match" );
101 return VLC_EGENERIC;
104 /* Reject 0 bpp and unsupported chroma */
105 const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
106 const vlc_chroma_description_t *p_chroma
107 = vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
108 if( !p_chroma || p_chroma->pixel_size == 0
109 || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
110 || !vlc_fourcc_IsYUV( fourcc ) )
112 msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
113 return VLC_EGENERIC;
116 /* Allocate structure */
117 p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) );
118 if( unlikely(!p_sys) )
119 return VLC_ENOMEM;
121 /* init data */
123 p_filter->pf_video_filter = Filter;
124 p_filter->pf_video_mouse = freeze_mouse;
126 return VLC_SUCCESS;
130 * Close the filter
132 static void Close( vlc_object_t *p_this ) {
133 filter_t *p_filter = (filter_t *)p_this;
134 filter_sys_t *p_sys = p_filter->p_sys;
136 /* Free allocated memory */
137 freeze_free_allocated_data( p_filter );
138 free( p_sys );
142 * Filter a picture
144 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
145 if( !p_pic_in || !p_filter) return NULL;
147 filter_sys_t *p_sys = p_filter->p_sys;
149 picture_t *p_pic_out = filter_NewPicture( p_filter );
150 if( unlikely(!p_pic_out) ) {
151 picture_Release( p_pic_in );
152 return NULL;
156 * allocate data
158 if ( unlikely(!p_sys->b_init) )
159 if (freeze_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS)
161 picture_Release( p_pic_in );
162 return NULL;
164 p_sys->b_init = true;
167 * preset output pic: raw copy src to dst
169 picture_CopyPixels(p_pic_out, p_pic_in);
172 * cache original pict pixels selected with mouse pointer
174 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
175 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
176 for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
178 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
179 / p_sys->i_height[i_p];
180 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
181 / p_sys->i_width[i_p];
183 if ( p_sys->pb_update_cache[i_Yr][i_Yc] )
184 p_sys->pi_freezed_picture[i_p][i_r][i_c]
185 = p_pic_in->p[i_p].p_pixels[i_r*p_pic_out->p[i_p].i_pitch
186 + i_c*p_pic_out->p[i_p].i_pixel_pitch];
190 * countdown freezed pixel delay & reset pb_update_cache flag
192 for ( int32_t i_Yr = 0; i_Yr < p_sys->i_height[Y_PLANE]; i_Yr++)
193 for ( int32_t i_Yc = 0; i_Yc < p_sys->i_width[Y_PLANE]; i_Yc++)
195 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
196 p_sys->pi_freezing_countdown[i_Yr][i_Yc]--;
197 p_sys->pb_update_cache[i_Yr][i_Yc] = false;
201 * apply filter: draw freezed pixels over current picture
203 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
204 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
205 for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
207 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
208 / p_sys->i_height[i_p];
209 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
210 / p_sys->i_width[i_p];
212 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
213 p_pic_out->p[i_p].p_pixels[i_r * p_pic_out->p[i_p].i_pitch
214 + i_c * p_pic_out->p[i_p].i_pixel_pitch]
215 = p_sys->pi_freezed_picture[i_p][i_r][i_c];
218 return CopyInfoAndRelease( p_pic_out, p_pic_in );
222 * mouse callback
224 static int freeze_mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
225 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
227 filter_sys_t *p_sys = p_filter->p_sys;
228 const video_format_t *p_fmt_in = &p_filter->fmt_in.video;
230 /* Only take events inside the video area */
231 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt_in->i_width ||
232 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt_in->i_height )
233 return VLC_EGENERIC;
235 if ( unlikely(!p_sys->b_init) )
237 *p_mouse = *p_new;
238 return VLC_SUCCESS;
241 int32_t i_base_timeout = 0;
242 if( vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT ) )
243 i_base_timeout = 100;
244 else if( vlc_mouse_IsLeftPressed( p_new ) )
245 i_base_timeout = 50;
247 if( i_base_timeout > 0 )
250 * find pixels selected by user to apply freezing filter
252 int32_t i_min_sq_radius = (p_sys->i_width[Y_PLANE] / 15)
253 * (p_sys->i_width[Y_PLANE] / 15);
254 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++)
255 for ( int32_t i_c = 0; i_c < p_sys->i_width[Y_PLANE]; i_c++)
257 int32_t i_sq_dist = ( p_new->i_x - i_c )
258 * ( p_new->i_x - i_c )
259 + ( p_new->i_y - i_r )
260 * ( p_new->i_y - i_r );
261 i_sq_dist = __MAX(0, i_sq_dist - i_min_sq_radius);
263 uint16_t i_timeout = __MAX(i_base_timeout - i_sq_dist, 0);
265 /* ask to update chache for pixel to be freezed just now */
266 if ( p_sys->pi_freezing_countdown[i_r][i_c] == 0 && i_timeout > 0)
267 p_sys->pb_update_cache[i_r][i_c] = true;
269 /* set freezing delay */
270 if ( p_sys->pi_freezing_countdown[i_r][i_c] < i_timeout )
271 p_sys->pi_freezing_countdown[i_r][i_c] = i_timeout;
275 return VLC_EGENERIC;
280 * Allocate data
282 static int freeze_allocate_data( filter_t *p_filter, picture_t *p_pic_in )
284 filter_sys_t *p_sys = p_filter->p_sys;
286 freeze_free_allocated_data( p_filter );
289 * take into account different characteristics for each plane
291 p_sys->i_planes = p_pic_in->i_planes;
292 p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
293 p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
294 p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
296 if ( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) )
298 freeze_free_allocated_data( p_filter );
299 return VLC_ENOMEM;
302 /* init data */
303 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
305 p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
306 p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
307 p_sys->i_width[i_p] = (int) p_pic_in->p[i_p].i_visible_pitch
308 / p_pic_in->p[i_p].i_pixel_pitch;
311 /* buffer used to countdown freezing delay */
312 p_sys->pi_freezing_countdown
313 = calloc( p_sys->i_height[Y_PLANE], sizeof(int16_t*) );
314 if ( unlikely( !p_sys->pi_freezing_countdown ) )
316 freeze_free_allocated_data( p_filter );
317 return VLC_ENOMEM;
320 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
322 p_sys->pi_freezing_countdown[i_r]
323 = calloc( p_sys->i_width[Y_PLANE], sizeof(int16_t) );
324 if ( unlikely( !p_sys->pi_freezing_countdown[i_r] ) )
326 freeze_free_allocated_data( p_filter );
327 return VLC_ENOMEM;
331 /* buffer used to cache freezed pixels colors */
332 p_sys->pi_freezed_picture = calloc( p_sys->i_planes, sizeof(int8_t**) );
333 if( unlikely( !p_sys->pi_freezed_picture ) )
335 freeze_free_allocated_data( p_filter );
336 return VLC_ENOMEM;
339 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++)
341 p_sys->pi_freezed_picture[i_p]
342 = calloc( p_sys->i_height[i_p], sizeof(int8_t*) );
343 if ( unlikely(!p_sys->pi_freezed_picture[i_p]) )
345 freeze_free_allocated_data( p_filter );
346 return VLC_ENOMEM;
348 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
350 p_sys->pi_freezed_picture[i_p][i_r]
351 = calloc( p_sys->i_width[i_p], sizeof(int8_t) );
352 if ( unlikely( !p_sys->pi_freezed_picture[i_p][i_r] ) )
354 freeze_free_allocated_data( p_filter );
355 return VLC_ENOMEM;
360 /* flag used to manage freezed pixels cache update */
361 p_sys->pb_update_cache
362 = calloc( p_sys->i_height[Y_PLANE], sizeof(bool*) );
363 if( unlikely( !p_sys->pb_update_cache ) )
365 freeze_free_allocated_data( p_filter );
366 return VLC_ENOMEM;
369 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
371 p_sys->pb_update_cache[i_r]
372 = calloc( p_sys->i_width[Y_PLANE], sizeof(bool) );
373 if ( unlikely( !p_sys->pb_update_cache[i_r] ) )
375 freeze_free_allocated_data( p_filter );
376 return VLC_ENOMEM;
380 return VLC_SUCCESS;
384 * Free allocated data
386 static void freeze_free_allocated_data( filter_t *p_filter ) {
387 filter_sys_t *p_sys = p_filter->p_sys;
389 if (p_sys->pi_freezing_countdown)
390 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
391 free( p_sys->pi_freezing_countdown[i_r] );
392 FREENULL( p_sys->pi_freezing_countdown );
394 if ( p_sys->pb_update_cache )
395 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
396 free( p_sys->pb_update_cache[i_r] );
397 FREENULL( p_sys->pb_update_cache );
399 if ( p_sys->pi_freezed_picture )
400 for ( int32_t i_p=0; i_p < p_sys->i_planes; i_p++ ) {
401 for ( int32_t i_r=0; i_r < p_sys->i_height[i_p]; i_r++ )
402 free( p_sys->pi_freezed_picture[i_p][i_r] );
403 free( p_sys->pi_freezed_picture[i_p] );
405 FREENULL( p_sys->pi_freezed_picture );
407 p_sys->i_planes = 0;
408 FREENULL( p_sys->i_height );
409 FREENULL( p_sys->i_width );
410 FREENULL( p_sys->i_visible_pitch );