A little more detail regarding using my github copies of the code with where it's...
[vlc/adversarial.git] / modules / video_filter / oldmovie.c
blobf97c4487fee4fc6cb3a08fee4a65f482989cfd9e
1 /*****************************************************************************
2 * oldmovie.c : Old movie effect 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 <math.h>
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_rand.h>
38 #include <vlc_mtime.h>
40 #include "filter_picture.h"
42 #ifndef M_PI
43 # define M_PI 3.14159265358979323846
44 #endif
45 #ifndef TIME_UNIT_PER_S
46 # define TIME_UNIT_PER_S ( ((int64_t) 1) << 32 )
47 #endif
49 static inline int64_t MOD(int64_t a, int64_t b) {
50 return ( ( a % b ) + b ) % b; }
52 #define SUB_MIN(val, sub_val, min) val = \
53 ((val-(int32_t)sub_val)<min?min:val-sub_val)
54 #define ADD_MAX(val, add_val, max) val = \
55 ((val+(int32_t)add_val)>max?max:val+add_val)
57 static inline int32_t PIX_OFS(int32_t i_x, int32_t i_y, plane_t *ps_plane) {
58 return i_x * ps_plane->i_pixel_pitch + i_y * ps_plane->i_pitch; }
60 #define CHECK_PIX_OFS(i_x, i_y, ps_plane) ( \
61 (i_x) >= 0 && (i_y) >= 0 && \
62 (i_x) * ps_plane->i_pixel_pitch < ps_plane->i_visible_pitch && \
63 (i_y) < ps_plane->i_visible_lines \
66 static inline void DARKEN_PIXEL(int32_t i_x, int32_t i_y,
67 int16_t intensity, plane_t *ps_plane) {
68 SUB_MIN( ps_plane->p_pixels[ PIX_OFS(i_x, i_y, ps_plane) ],
69 intensity, 0 );
72 static inline void LIGHTEN_PIXEL(int32_t i_x, int32_t i_y,
73 int16_t intensity, plane_t *ps_plane) {
74 ADD_MAX( ps_plane->p_pixels[ PIX_OFS(i_x, i_y, ps_plane) ],
75 intensity, 0xFF );
78 static inline void CHECK_N_DARKEN_PIXEL(int32_t i_x, int32_t i_y,
79 int16_t intensity, plane_t *ps_plane) {
80 if ( likely( CHECK_PIX_OFS(i_x, i_y, ps_plane) ) )
81 DARKEN_PIXEL(i_x, i_y, intensity, ps_plane);
84 static inline void CHECK_N_LIGHTEN_PIXEL(int32_t i_x, int32_t i_y,
85 int16_t intensity, plane_t *ps_plane) {
86 if ( likely( CHECK_PIX_OFS(i_x, i_y, ps_plane) ) )
87 LIGHTEN_PIXEL(i_x, i_y, intensity, ps_plane);
90 #define MAX_SCRATCH 20
91 #define MAX_HAIR 10
92 #define MAX_DUST 10
94 typedef struct {
95 int32_t i_offset;
96 int32_t i_width;
97 uint16_t i_intensity;
98 uint64_t i_stop_trigger;
99 } scratch_t;
101 typedef struct {
102 int32_t i_x, i_y;
103 uint8_t i_rotation;
104 int32_t i_width;
105 int32_t i_length;
106 int32_t i_curve;
107 uint16_t i_intensity;
108 uint64_t i_stop_trigger;
109 } hair_t;
111 typedef struct {
112 int32_t i_x, i_y;
113 int32_t i_width;
114 uint16_t i_intensity;
115 uint64_t i_stop_trigger;
116 } dust_t;
118 struct filter_sys_t {
120 /* general data */
121 bool b_init;
122 int32_t i_planes;
123 int32_t *i_height;
124 int32_t *i_width;
125 int32_t *i_visible_pitch;
126 uint64_t i_start_time;
127 uint64_t i_last_time;
128 uint64_t i_cur_time;
130 /* sliding & offset effect */
131 uint64_t i_offset_trigger;
132 uint64_t i_sliding_trigger;
133 uint64_t i_sliding_stop_trig;
134 int32_t i_offset_ofs;
135 int32_t i_sliding_ofs;
136 int32_t i_sliding_speed;
138 /* scratch on film */
139 uint64_t i_scratch_trigger;
140 scratch_t *p_scratch[MAX_SCRATCH];
142 /* hair on lens */
143 uint64_t i_hair_trigger;
144 hair_t *p_hair[MAX_HAIR];
146 /* blotch on film */
147 uint64_t i_blotch_trigger;
149 /* dust on lens */
150 uint64_t i_dust_trigger;
151 dust_t *p_dust[MAX_DUST];
154 /*****************************************************************************
155 * Prototypes
156 *****************************************************************************/
158 static picture_t *Filter( filter_t *, picture_t * );
160 static int oldmovie_allocate_data( filter_t *, picture_t * );
161 static void oldmovie_free_allocated_data( filter_t * );
163 static void oldmovie_shutter_effect( filter_t *, picture_t * );
164 static int oldmovie_sliding_offset_effect( filter_t *, picture_t * );
165 static void oldmovie_black_n_white_effect( picture_t * );
166 static int oldmovie_dark_border_effect( filter_t *, picture_t * );
167 static int oldmovie_film_scratch_effect( filter_t *, picture_t * );
168 static void oldmovie_film_blotch_effect( filter_t *, picture_t * );
169 static void oldmovie_film_dust_effect( filter_t *, picture_t * );
170 static int oldmovie_lens_hair_effect( filter_t *, picture_t * );
171 static int oldmovie_lens_dust_effect( filter_t *, picture_t * );
173 static void oldmovie_define_hair_location( filter_t *p_filter, hair_t* ps_hair );
174 static void oldmovie_define_dust_location( filter_t *p_filter, dust_t* ps_dust );
175 static int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out );
177 /*****************************************************************************
178 * Module descriptor
179 *****************************************************************************/
181 static int Open ( vlc_object_t * );
182 static void Close( vlc_object_t * );
184 vlc_module_begin()
185 set_description( N_("Old movie effect video filter") )
186 set_shortname( N_( "Old movie" ))
187 set_capability( "video filter2", 0 )
188 set_category( CAT_VIDEO )
189 set_subcategory( SUBCAT_VIDEO_VFILTER )
191 set_callbacks( Open, Close )
192 vlc_module_end()
195 * Open the filter
197 static int Open( vlc_object_t *p_this ) {
198 filter_t *p_filter = (filter_t *)p_this;
199 filter_sys_t *p_sys;
201 /* Assert video in match with video out */
202 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
203 msg_Err( p_filter, "Input and output format does not match" );
204 return VLC_EGENERIC;
207 /* Reject 0 bpp and unsupported chroma */
208 const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
209 const vlc_chroma_description_t *p_chroma =
210 vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
211 if( !p_chroma || p_chroma->pixel_size == 0
212 || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
213 || !vlc_fourcc_IsYUV( fourcc ) ) {
215 msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
216 return VLC_EGENERIC;
219 /* Allocate structure */
220 p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
221 if( unlikely( !p_sys ) )
222 return VLC_ENOMEM;
224 /* init data */
225 p_filter->pf_video_filter = Filter;
226 p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = NTPtime64();
228 return VLC_SUCCESS;
232 * Close the filter
234 static void Close( vlc_object_t *p_this ) {
235 filter_t *p_filter = (filter_t *)p_this;
236 filter_sys_t *p_sys = p_filter->p_sys;
238 /* Free allocated memory */
239 oldmovie_free_allocated_data( p_filter );
240 free( p_sys );
244 * Filter a picture
246 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
247 if( unlikely( !p_pic_in || !p_filter ) )
248 return NULL;
250 filter_sys_t *p_sys = p_filter->p_sys;
252 picture_t *p_pic_out = filter_NewPicture( p_filter );
253 if( unlikely( !p_pic_out ) ) {
254 picture_Release( p_pic_in );
255 return NULL;
259 * manage time
261 p_sys->i_last_time = p_sys->i_cur_time;
262 p_sys->i_cur_time = NTPtime64();
265 * allocate data
267 if ( unlikely( !p_sys->b_init ) )
268 if ( unlikely( oldmovie_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
269 picture_Release( p_pic_in );
270 return NULL;
272 p_sys->b_init = true;
275 * preset output pic: raw copy src to dst
277 picture_CopyPixels(p_pic_out, p_pic_in);
280 * apply several effects on picture
282 oldmovie_black_n_white_effect( p_pic_out );
284 /* simulates projector shutter blinking effect */
285 oldmovie_shutter_effect(p_filter, p_pic_out);
287 if ( unlikely( oldmovie_sliding_offset_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
288 return CopyInfoAndRelease( p_pic_out, p_pic_in );
290 oldmovie_dark_border_effect( p_filter, p_pic_out );
292 if ( unlikely( oldmovie_film_scratch_effect(p_filter, p_pic_out) != VLC_SUCCESS ) )
293 return CopyInfoAndRelease( p_pic_out, p_pic_in );
295 oldmovie_film_blotch_effect(p_filter, p_pic_out);
297 if ( unlikely( oldmovie_lens_hair_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
298 return CopyInfoAndRelease( p_pic_out, p_pic_in );
300 if ( unlikely( oldmovie_lens_dust_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
301 return CopyInfoAndRelease( p_pic_out, p_pic_in );
303 oldmovie_film_dust_effect( p_filter, p_pic_out );
305 return CopyInfoAndRelease( p_pic_out, p_pic_in );
309 * Allocate data
311 static int oldmovie_allocate_data( filter_t *p_filter, picture_t *p_pic_in ) {
312 filter_sys_t *p_sys = p_filter->p_sys;
314 oldmovie_free_allocated_data( p_filter );
317 * take into account different characteristics for each plane
319 p_sys->i_planes = p_pic_in->i_planes;
320 p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
321 p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
322 p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
324 if( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) ) {
325 oldmovie_free_allocated_data( p_filter );
326 return VLC_ENOMEM;
329 for (int32_t i_p=0; i_p < p_sys->i_planes; i_p++) {
330 p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
331 p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
332 p_sys->i_width [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch
333 / p_pic_in->p[i_p].i_pixel_pitch;
335 return VLC_SUCCESS;
339 * Free allocated data
341 static void oldmovie_free_allocated_data( filter_t *p_filter ) {
342 filter_sys_t *p_sys = p_filter->p_sys;
344 for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
345 FREENULL(p_sys->p_scratch[i_s]);
347 for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
348 FREENULL(p_sys->p_hair[i_h]);
350 for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
351 FREENULL(p_sys->p_dust[i_d]);
353 p_sys->i_planes = 0;
354 FREENULL( p_sys->i_height );
355 FREENULL( p_sys->i_width );
356 FREENULL( p_sys->i_visible_pitch );
360 * Projector shutter effect
362 static void oldmovie_shutter_effect( filter_t *p_filter, picture_t *p_pic_out ) {
363 filter_sys_t *p_sys = p_filter->p_sys;
365 #define SHUTTER_FREQ 2
366 #define SHUTTER_SPEED 25
367 #define SHUTTER_HEIGHT 1.5
369 #define SHUTTER_INTENSITY 50
371 #define SUB_FRAME (p_sys->i_cur_time % (TIME_UNIT_PER_S / SHUTTER_FREQ))
374 * depending on current time: define shutter location on picture
376 int32_t i_shutter_sup = VLC_CLIP((int64_t)SUB_FRAME
377 * p_pic_out->p[Y_PLANE].i_visible_lines
378 * SHUTTER_SPEED / TIME_UNIT_PER_S,
379 0, p_pic_out->p[Y_PLANE].i_visible_lines);
381 int32_t i_shutter_inf = VLC_CLIP((int64_t)SUB_FRAME
382 * p_pic_out->p[Y_PLANE].i_visible_lines
383 * SHUTTER_SPEED / TIME_UNIT_PER_S
384 - SHUTTER_HEIGHT * p_pic_out->p[Y_PLANE].i_visible_lines,
385 0, p_pic_out->p[Y_PLANE].i_visible_lines);
387 int32_t i_width = p_pic_out->p[Y_PLANE].i_visible_pitch
388 / p_pic_out->p[Y_PLANE].i_pixel_pitch;
391 * darken pixels currently occulted by shutter
393 for ( int32_t i_y = i_shutter_inf; i_y < i_shutter_sup; i_y++ )
394 for ( int32_t i_x = 0; i_x < i_width; i_x++ )
395 DARKEN_PIXEL( i_x, i_y, SHUTTER_INTENSITY, &p_pic_out->p[Y_PLANE] );
399 * sliding & offset effect
401 static int oldmovie_sliding_offset_effect( filter_t *p_filter, picture_t *p_pic_out ) {
402 filter_sys_t *p_sys = p_filter->p_sys;
406 * one shot offset section
409 #define OFFSET_AVERAGE_PERIOD (10 * TIME_UNIT_PER_S)
411 /* start trigger to be (re)initialized */
412 if ( p_sys->i_offset_trigger == 0
413 || p_sys->i_sliding_speed != 0 ) { /* do not mix sliding and offset */
414 /* random trigger for offset effect */
415 p_sys->i_offset_trigger = p_sys->i_cur_time
416 + ( (uint64_t) vlc_mrand48() ) % OFFSET_AVERAGE_PERIOD
417 + OFFSET_AVERAGE_PERIOD / 2;
418 p_sys->i_offset_ofs = 0;
419 } else if ( p_sys->i_offset_trigger <= p_sys->i_cur_time ) {
420 /* trigger for offset effect */
421 p_sys->i_offset_trigger = 0;
422 p_sys->i_offset_ofs = MOD( ( (uint32_t)vlc_mrand48() ),
423 p_sys->i_height[Y_PLANE] ) * 100;
424 } else
425 p_sys->i_offset_ofs = 0;
429 * sliding section
432 #define SLIDING_AVERAGE_PERIOD (20 * TIME_UNIT_PER_S)
433 #define SLIDING_AVERAGE_DURATION ( 3 * TIME_UNIT_PER_S)
435 /* start trigger to be (re)initialized */
436 if ( ( p_sys->i_sliding_stop_trig == 0 )
437 && ( p_sys->i_sliding_trigger == 0 )
438 && ( p_sys->i_sliding_speed == 0 ) ) {
439 /* random trigger which enable sliding effect */
440 p_sys->i_sliding_trigger = p_sys->i_cur_time
441 + ( (uint64_t) vlc_mrand48() ) % SLIDING_AVERAGE_PERIOD
442 + SLIDING_AVERAGE_PERIOD / 2;
444 /* start trigger just occurs */
445 else if ( ( p_sys->i_sliding_stop_trig == 0 )
446 && ( p_sys->i_sliding_trigger <= p_sys->i_cur_time )
447 && ( p_sys->i_sliding_speed == 0 ) ) {
448 /* init sliding parameters */
449 p_sys->i_sliding_trigger = 0;
450 p_sys->i_sliding_stop_trig = p_sys->i_cur_time
451 + ((uint64_t) vlc_mrand48() ) % SLIDING_AVERAGE_DURATION
452 + SLIDING_AVERAGE_DURATION / 2;
453 p_sys->i_sliding_ofs = 0;
454 /* note: sliding speed unit = image per 100 s */
455 p_sys->i_sliding_speed = MOD(((int32_t) vlc_mrand48() ), 201) - 100;
457 /* stop trigger disabling sliding effect */
458 else if ( ( p_sys->i_sliding_stop_trig <= p_sys->i_cur_time )
459 && ( p_sys->i_sliding_trigger == 0 ) ) {
461 /* first increase speed to ensure we will come back to stable image */
462 if ( abs(p_sys->i_sliding_speed) < 50 )
463 p_sys->i_sliding_speed += 5;
465 /* check if offset is close to 0 and then ready to stop */
466 if ( abs( p_sys->i_sliding_ofs ) < abs( p_sys->i_sliding_speed
467 * p_sys->i_height[Y_PLANE]
468 * ( p_sys->i_cur_time - p_sys->i_last_time ) / TIME_UNIT_PER_S )
469 || abs( p_sys->i_sliding_ofs ) < p_sys->i_height[Y_PLANE] * 100 / 20 ) {
471 /* reset sliding parameters */
472 p_sys->i_sliding_ofs = p_sys->i_sliding_speed = 0;
473 p_sys->i_sliding_trigger = p_sys->i_sliding_stop_trig = 0;
477 /* update offset */
478 p_sys->i_sliding_ofs += p_sys->i_sliding_speed * p_sys->i_height[Y_PLANE]
479 * ( p_sys->i_cur_time - p_sys->i_last_time )
480 / TIME_UNIT_PER_S;
482 p_sys->i_sliding_ofs = MOD( p_sys->i_sliding_ofs,
483 p_sys->i_height[Y_PLANE] * 100 );
485 /* apply offset */
486 return oldmovie_sliding_offset_apply( p_filter, p_pic_out );
490 * apply both sliding and offset effect
492 static int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out )
494 filter_sys_t *p_sys = p_filter->p_sys;
496 for ( uint8_t i_p = 0; i_p < p_pic_out->i_planes; i_p++ ) {
497 /* first allocate temporary buffer for swap operation */
498 uint8_t *p_temp_buf = calloc( p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch,
499 sizeof(uint8_t) );
500 if ( unlikely( !p_temp_buf ) )
501 return VLC_ENOMEM;
502 memcpy( p_temp_buf,p_pic_out->p[i_p].p_pixels,
503 p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch );
505 /* copy lines to output_pic */
506 for ( int32_t i_y = 0; i_y < p_pic_out->p[i_p].i_visible_lines; i_y++ ) {
507 int32_t i_ofs = MOD( ( p_sys->i_offset_ofs + p_sys->i_sliding_ofs )
508 /100,
509 p_sys->i_height[Y_PLANE] );
510 i_ofs *= p_pic_out->p[i_p].i_visible_lines;
511 i_ofs /= p_sys->i_height[Y_PLANE];
513 memcpy( &p_pic_out->p[i_p].p_pixels[ i_y * p_pic_out->p[i_p].i_pitch ],
514 &p_temp_buf[ ( ( i_y + i_ofs ) % p_pic_out->p[i_p].i_visible_lines ) * p_pic_out->p[i_p].i_pitch ],
515 p_pic_out->p[i_p].i_visible_pitch);
517 free( p_temp_buf );
520 return VLC_SUCCESS;
524 * Black and white transform including a touch of sepia effect
526 static void oldmovie_black_n_white_effect( picture_t *p_pic_out )
528 for ( int32_t i_y = 0; i_y < p_pic_out->p[Y_PLANE].i_visible_lines; i_y++ )
529 for ( int32_t i_x = 0; i_x < p_pic_out->p[Y_PLANE].i_visible_pitch;
530 i_x += p_pic_out->p[Y_PLANE].i_pixel_pitch ) {
532 uint32_t i_pix_ofs = i_x+i_y*p_pic_out->p[Y_PLANE].i_pitch;
533 p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] -= p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] >> 2;
534 p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] += 30;
537 memset( p_pic_out->p[U_PLANE].p_pixels, 122,
538 p_pic_out->p[U_PLANE].i_lines * p_pic_out->p[U_PLANE].i_pitch );
539 memset( p_pic_out->p[V_PLANE].p_pixels, 132,
540 p_pic_out->p[V_PLANE].i_lines * p_pic_out->p[V_PLANE].i_pitch );
544 * Smooth darker borders effect
546 static int oldmovie_dark_border_effect( filter_t *p_filter, picture_t *p_pic_out )
548 filter_sys_t *p_sys = p_filter->p_sys;
550 #define BORDER_DIST 20
552 for ( int32_t i_y = 0; i_y < p_sys->i_height[Y_PLANE]; i_y++ )
553 for ( int32_t i_x = 0; i_x < p_sys->i_width[Y_PLANE]; i_x++ ) {
555 int32_t i_x_border_dist = __MIN( i_x, p_sys->i_width[Y_PLANE] - i_x);
556 int32_t i_y_border_dist = __MIN( i_y, p_sys->i_height[Y_PLANE] - i_y);
558 int32_t i_border_dist = __MAX(BORDER_DIST - i_x_border_dist,0)
559 + __MAX(BORDER_DIST - i_y_border_dist,0);
561 i_border_dist = __MIN(BORDER_DIST, i_border_dist);
563 if ( i_border_dist == 0 )
564 continue;
566 uint32_t i_pix_ofs = i_x * p_pic_out->p[Y_PLANE].i_pixel_pitch
567 + i_y * p_pic_out->p[Y_PLANE].i_pitch;
569 SUB_MIN( p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs],
570 i_border_dist * 255 / BORDER_DIST, 0 );
573 return VLC_SUCCESS;
577 * Vertical scratch random management and effect
579 static int oldmovie_film_scratch_effect( filter_t *p_filter, picture_t *p_pic_out )
581 filter_sys_t *p_sys = p_filter->p_sys;
583 #define SCRATCH_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 2 )
584 #define SCRATCH_DURATION ( TIME_UNIT_PER_S * 1 / 2)
586 /* generate new scratch */
587 if ( p_sys->i_scratch_trigger <= p_sys->i_cur_time ) {
588 for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
589 if ( p_sys->p_scratch[i_s] == NULL ) {
590 /* allocate data */
591 p_sys->p_scratch[i_s] = calloc( 1, sizeof(scratch_t) );
592 if ( unlikely( !p_sys->p_scratch[i_s] ) )
593 return VLC_ENOMEM;
595 /* set random parameters */
596 p_sys->p_scratch[i_s]->i_offset = ( ( (unsigned)vlc_mrand48() )
597 % __MAX( p_sys->i_width[Y_PLANE] - 10, 1 ) )
598 + 5;
599 p_sys->p_scratch[i_s]->i_width = ( ( (unsigned)vlc_mrand48() )
600 % __MAX( p_sys->i_width[Y_PLANE] / 500, 1 ) )
601 + 1;
602 p_sys->p_scratch[i_s]->i_intensity = (unsigned) vlc_mrand48() % 50 + 10;
603 p_sys->p_scratch[i_s]->i_stop_trigger = p_sys->i_cur_time
604 + (uint64_t) vlc_mrand48() % SCRATCH_DURATION
605 + SCRATCH_DURATION / 2;
607 break;
609 p_sys->i_scratch_trigger = p_sys->i_cur_time
610 + ( (uint64_t)vlc_mrand48() ) % SCRATCH_GENERATOR_PERIOD
611 + SCRATCH_GENERATOR_PERIOD / 2;
614 /* manage and apply current scratch */
615 for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
616 if ( p_sys->p_scratch[i_s] ) {
617 /* remove outdated scratch */
618 if ( p_sys->p_scratch[i_s]->i_stop_trigger <= p_sys->i_cur_time ) {
619 FREENULL( p_sys->p_scratch[i_s] );
620 continue;
623 /* otherwise apply scratch */
624 for ( int32_t i_y = 0; i_y < p_pic_out->p[Y_PLANE].i_visible_lines; i_y++ )
625 for ( int32_t i_x = p_sys->p_scratch[i_s]->i_offset;
626 i_x < __MIN(p_sys->p_scratch[i_s]->i_offset
627 + p_sys->p_scratch[i_s]->i_width, p_sys->i_width[Y_PLANE] );
628 i_x++ )
629 DARKEN_PIXEL( i_x, i_y, p_sys->p_scratch[i_s]->i_intensity,
630 &p_pic_out->p[Y_PLANE] );
633 return VLC_SUCCESS;
637 * Blotch addition
638 * bigger than dust but only during one frame (due to a local film damage)
640 static void oldmovie_film_blotch_effect( filter_t *p_filter, picture_t *p_pic_out )
642 filter_sys_t *p_sys = p_filter->p_sys;
644 #define BLOTCH_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 5 )
646 /* generate blotch */
647 if ( p_sys->i_blotch_trigger <= p_sys->i_cur_time ) {
648 /* set random parameters */
649 int32_t i_bx = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
650 int32_t i_by = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
651 int32_t i_width = (unsigned)vlc_mrand48() % __MAX( 1, p_sys->i_width[Y_PLANE] / 10 ) + 1;
652 int32_t i_intensity = (unsigned)vlc_mrand48() % 50 + 20;
654 if ( (unsigned)vlc_mrand48() & 0x01 ) {
655 /* draw dark blotch */
656 for ( int32_t i_y = -i_width + 1; i_y < i_width; i_y++ )
657 for ( int32_t i_x = -i_width + 1; i_x < i_width; i_x++ )
658 if ( i_x * i_x + i_y * i_y <= i_width * i_width )
659 CHECK_N_DARKEN_PIXEL( i_x + i_bx, i_y + i_by,
660 i_intensity, &p_pic_out->p[Y_PLANE] );
661 } else {
662 /* draw light blotch */
663 for ( int32_t i_y = -i_width+1; i_y < i_width; i_y++ )
664 for ( int32_t i_x = -i_width+1; i_x < i_width; i_x++ )
665 if ( i_x * i_x + i_y * i_y <= i_width * i_width )
666 CHECK_N_LIGHTEN_PIXEL( i_x + i_bx, i_y + i_by,
667 i_intensity, &p_pic_out->p[Y_PLANE] );
670 p_sys->i_blotch_trigger = p_sys->i_cur_time
671 + (uint64_t)vlc_mrand48() % BLOTCH_GENERATOR_PERIOD
672 + BLOTCH_GENERATOR_PERIOD / 2;
677 * Dust dots addition, visible during one frame only (film damages)
679 static void oldmovie_film_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
680 #define ONESHOT_DUST_RATIO 1000
682 filter_sys_t *p_sys = p_filter->p_sys;
684 for ( int32_t i_dust = 0;
685 i_dust < p_sys->i_width[Y_PLANE] * p_sys->i_height[Y_PLANE] / ONESHOT_DUST_RATIO;
686 i_dust++)
687 if ( (unsigned)vlc_mrand48() % 5 < 3 )
688 DARKEN_PIXEL( (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE],
689 (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE],
690 150, &p_pic_out->p[Y_PLANE] );
691 else
692 LIGHTEN_PIXEL( (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE],
693 (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE],
694 50, &p_pic_out->p[Y_PLANE] );
698 * Hair and dust on projector lens
701 #define HAIR_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 50 )
702 #define HAIR_DURATION ( TIME_UNIT_PER_S * 50 )
703 #define DUST_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 100 )
704 #define DUST_DURATION ( TIME_UNIT_PER_S * 4 )
707 * Define hair location on the lens and timeout
710 static void oldmovie_define_hair_location( filter_t *p_filter, hair_t* ps_hair ) {
711 filter_sys_t *p_sys = p_filter->p_sys;
713 ps_hair->i_x = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
714 ps_hair->i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
715 ps_hair->i_rotation = (unsigned)vlc_mrand48() % 200;
717 ps_hair->i_stop_trigger = p_sys->i_cur_time
718 + (uint64_t)vlc_mrand48() % HAIR_DURATION
719 + HAIR_DURATION / 2;
723 * Show black hair on the screen
724 * after random duration it is removed or re-located
726 static int oldmovie_lens_hair_effect( filter_t *p_filter, picture_t *p_pic_out ) {
727 filter_sys_t *p_sys = p_filter->p_sys;
729 /* generate new hair */
730 if ( p_sys->i_hair_trigger <= p_sys->i_cur_time ) {
731 for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
732 if ( p_sys->p_hair[i_h] == NULL ) {
733 /* allocate data */
734 p_sys->p_hair[i_h] = calloc( 1, sizeof(hair_t) );
735 if ( unlikely( !p_sys->p_hair[i_h] ) )
736 return VLC_ENOMEM;
738 /* set random parameters */
739 p_sys->p_hair[i_h]->i_length = (unsigned)vlc_mrand48()
740 % ( p_sys->i_width[Y_PLANE] / 3 ) + 5;
741 p_sys->p_hair[i_h]->i_curve = MOD( (int32_t)vlc_mrand48(), 80 ) - 40;
742 p_sys->p_hair[i_h]->i_width = (unsigned)vlc_mrand48()
743 % __MAX( 1, p_sys->i_width[Y_PLANE] / 1500 ) + 1;
744 p_sys->p_hair[i_h]->i_intensity = (unsigned)vlc_mrand48() % 50 + 20;
746 oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
748 break;
750 p_sys->i_hair_trigger = p_sys->i_cur_time
751 + (uint64_t)vlc_mrand48() % HAIR_GENERATOR_PERIOD
752 + HAIR_GENERATOR_PERIOD / 2;
755 /* manage and apply current hair */
756 for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
757 if ( p_sys->p_hair[i_h] ) {
758 /* remove outdated ones */
759 if ( p_sys->p_hair[i_h]->i_stop_trigger <= p_sys->i_cur_time ) {
760 /* select between moving or removing hair */
761 if ( (unsigned)vlc_mrand48() % 2 == 0 )
762 /* move hair */
763 oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
764 else {
765 /* remove hair */
766 FREENULL( p_sys->p_hair[i_h] );
767 continue;
771 /* draw hair */
772 double f_base_x = (double)p_sys->p_hair[i_h]->i_x;
773 double f_base_y = (double)p_sys->p_hair[i_h]->i_y;
775 for ( int32_t i_l = 0; i_l < p_sys->p_hair[i_h]->i_length; i_l++ ) {
776 uint32_t i_current_rot = p_sys->p_hair[i_h]->i_rotation
777 + p_sys->p_hair[i_h]->i_curve * i_l / 100;
778 f_base_x += cos( (double)i_current_rot / 128.0 * M_PI );
779 f_base_y += sin( (double)i_current_rot / 128.0 * M_PI );
780 double f_current_x = f_base_x;
781 double f_current_y = f_base_y;
782 for ( int32_t i_w = 0; i_w < p_sys->p_hair[i_h]->i_width; i_w++ ) {
783 f_current_x += sin( (double)i_current_rot / 128.0 * M_PI );
784 f_current_y += cos( (double)i_current_rot / 128.0 * M_PI );
785 CHECK_N_DARKEN_PIXEL( (int32_t) f_current_x,
786 (int32_t) f_current_y,
787 p_sys->p_hair[i_h]->i_intensity,
788 &p_pic_out->p[Y_PLANE] );
793 return VLC_SUCCESS;
797 * Define dust location on the lens and timeout
800 static void oldmovie_define_dust_location( filter_t *p_filter, dust_t* ps_dust ) {
801 filter_sys_t *p_sys = p_filter->p_sys;
803 ps_dust->i_x = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
804 ps_dust->i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
806 ps_dust->i_stop_trigger = p_sys->i_cur_time
807 + (uint64_t)vlc_mrand48() % HAIR_DURATION
808 + HAIR_DURATION / 2;
811 ps_dust->i_x = MOD( (int32_t)vlc_mrand48(), p_sys->i_width[Y_PLANE] );
812 ps_dust->i_y = MOD( (int32_t)vlc_mrand48(), p_sys->i_height[Y_PLANE] );
814 ps_dust->i_stop_trigger = p_sys->i_cur_time
815 + (uint64_t)vlc_mrand48() % DUST_DURATION
816 + DUST_DURATION / 2;
820 * Dust addition
821 * smaller than blotch but will remain on the screen for long time
823 static int oldmovie_lens_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
824 filter_sys_t *p_sys = p_filter->p_sys;
826 /* generate new dust */
827 if ( p_sys->i_dust_trigger <= p_sys->i_cur_time ) {
828 for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
829 if ( p_sys->p_dust[i_d] == NULL ) {
830 /* allocate data */
831 p_sys->p_dust[i_d] = calloc( 1, sizeof(dust_t) );
832 if ( unlikely( !p_sys->p_dust[i_d] ) )
833 return VLC_ENOMEM;
835 /* set random parameters */
836 oldmovie_define_dust_location( p_filter, p_sys->p_dust[i_d] );
837 p_sys->p_dust[i_d]->i_width = MOD( (int32_t)vlc_mrand48(), 5 ) + 1;
838 p_sys->p_dust[i_d]->i_intensity = (unsigned)vlc_mrand48() % 30 + 30;
840 break;
842 p_sys->i_dust_trigger = p_sys->i_cur_time
843 + (uint64_t)vlc_mrand48() % DUST_GENERATOR_PERIOD
844 + DUST_GENERATOR_PERIOD / 2;
847 /* manage and apply current dust */
848 for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
849 if ( p_sys->p_dust[i_d] ) {
850 /* remove outdated ones */
851 if ( p_sys->p_dust[i_d]->i_stop_trigger <= p_sys->i_cur_time ) {
852 /* select between moving or removing dust */
853 if ( (unsigned)vlc_mrand48() % 2 == 0 )
854 /* move dust */
855 oldmovie_define_dust_location( p_filter, p_sys->p_dust[i_d] );
856 else {
857 /* remove dust */
858 FREENULL( p_sys->p_dust[i_d] );
859 continue;
863 /* draw dust */
864 for ( int32_t i_y = -p_sys->p_dust[i_d]->i_width + 1; i_y < p_sys->p_dust[i_d]->i_width; i_y++ )
865 for ( int32_t i_x = -p_sys->p_dust[i_d]->i_width + 1; i_x < p_sys->p_dust[i_d]->i_width; i_x++ )
866 if ( i_x * i_x + i_y * i_y <= p_sys->p_dust[i_d]->i_width * p_sys->p_dust[i_d]->i_width )
867 CHECK_N_DARKEN_PIXEL( i_x + p_sys->p_dust[i_d]->i_x,
868 i_y + p_sys->p_dust[i_d]->i_y,
869 p_sys->p_dust[i_d]->i_intensity,
870 &p_pic_out->p[Y_PLANE] );
873 return VLC_SUCCESS;