]> git.sesse.net Git - vlc/blob - modules/video_filter/oldmovie.c
decoder: fix data race in input_DecoderChangePause()
[vlc] / modules / video_filter / oldmovie.c
1 /*****************************************************************************
2  * oldmovie.c : Old movie effect video filter
3  *****************************************************************************
4  * Copyright (C) 2013      Vianney Boyer
5  * $Id$
6  *
7  * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
8  *
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.
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 Lesser General Public License for more details.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <math.h>
33
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>
39
40 #include "filter_picture.h"
41
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
48
49 static inline int64_t MOD(int64_t a, int64_t b) {
50     return ( ( a % b ) + b ) % b; }
51
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)
56
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; }
59
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 \
64     )
65
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 );
70 }
71
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 );
76 }
77
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);
82 }
83
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);
88 }
89
90 #define MAX_SCRATCH        20
91 #define MAX_HAIR           10
92 #define MAX_DUST           10
93
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;
100
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;
110
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;
117
118 struct filter_sys_t {
119
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;
129
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;
137
138     /* scratch on film */
139     uint64_t   i_scratch_trigger;
140     scratch_t *p_scratch[MAX_SCRATCH];
141
142     /* hair on lens */
143     uint64_t   i_hair_trigger;
144     hair_t    *p_hair[MAX_HAIR];
145
146     /* blotch on film */
147     uint64_t   i_blotch_trigger;
148
149     /* dust on lens */
150     uint64_t   i_dust_trigger;
151     dust_t    *p_dust[MAX_DUST];
152 };
153
154 /*****************************************************************************
155  * Prototypes
156  *****************************************************************************/
157
158 static picture_t *Filter( filter_t *, picture_t * );
159
160 static int  oldmovie_allocate_data( filter_t *, picture_t * );
161 static void oldmovie_free_allocated_data( filter_t * );
162
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 * );
172
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 );
176
177 /*****************************************************************************
178  * Module descriptor
179  *****************************************************************************/
180
181 static int  Open ( vlc_object_t * );
182 static void Close( vlc_object_t * );
183
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 )
190
191     set_callbacks( Open, Close )
192 vlc_module_end()
193
194 /**
195  * Open the filter
196  */
197 static int Open( vlc_object_t *p_this ) {
198     filter_t *p_filter = (filter_t *)p_this;
199     filter_sys_t *p_sys;
200
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;
205     }
206
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 ) ) {
214
215         msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
216         return VLC_EGENERIC;
217     }
218
219     /* Allocate structure */
220     p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
221     if( unlikely( !p_sys ) )
222         return VLC_ENOMEM;
223
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();
227
228     return VLC_SUCCESS;
229 }
230
231 /**
232  * Close the filter
233  */
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;
237
238     /* Free allocated memory */
239     oldmovie_free_allocated_data( p_filter );
240     free( p_sys );
241 }
242
243 /**
244  * Filter a picture
245  */
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;
249
250     filter_sys_t *p_sys = p_filter->p_sys;
251
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;
256     }
257
258    /*
259     * manage time
260     */
261     p_sys->i_last_time = p_sys->i_cur_time;
262     p_sys->i_cur_time  = NTPtime64();
263
264    /*
265     * allocate data
266     */
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;
271         }
272     p_sys->b_init = true;
273
274    /*
275     * preset output pic: raw copy src to dst
276     */
277     picture_CopyPixels(p_pic_out, p_pic_in);
278
279    /*
280     * apply several effects on picture
281     */
282     oldmovie_black_n_white_effect( p_pic_out );
283
284     /* simulates projector shutter blinking effect */
285     oldmovie_shutter_effect(p_filter, p_pic_out);
286
287     if ( unlikely( oldmovie_sliding_offset_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
288         return CopyInfoAndRelease( p_pic_out, p_pic_in );
289
290     oldmovie_dark_border_effect( p_filter, p_pic_out );
291
292     if ( unlikely( oldmovie_film_scratch_effect(p_filter, p_pic_out) != VLC_SUCCESS ) )
293         return CopyInfoAndRelease( p_pic_out, p_pic_in );
294
295     oldmovie_film_blotch_effect(p_filter, p_pic_out);
296
297     if ( unlikely( oldmovie_lens_hair_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
298         return CopyInfoAndRelease( p_pic_out, p_pic_in );
299
300     if ( unlikely( oldmovie_lens_dust_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
301         return CopyInfoAndRelease( p_pic_out, p_pic_in );
302
303     oldmovie_film_dust_effect( p_filter, p_pic_out );
304
305     return CopyInfoAndRelease( p_pic_out, p_pic_in );
306 }
307
308 /*
309  * Allocate data
310  */
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;
313
314     oldmovie_free_allocated_data( p_filter );
315
316    /*
317     * take into account different characteristics for each plane
318     */
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) );
323
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;
327     }
328
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;
334     }
335     return VLC_SUCCESS;
336 }
337
338 /**
339  * Free allocated data
340  */
341 static void oldmovie_free_allocated_data( filter_t *p_filter ) {
342     filter_sys_t *p_sys = p_filter->p_sys;
343
344     for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
345         FREENULL(p_sys->p_scratch[i_s]);
346
347     for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
348         FREENULL(p_sys->p_hair[i_h]);
349
350     for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
351         FREENULL(p_sys->p_dust[i_d]);
352
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 );
357 }
358
359 /**
360  * Projector shutter effect
361  */
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;
364
365 #define SHUTTER_FREQ      2
366 #define SHUTTER_SPEED     25
367 #define SHUTTER_HEIGHT    1.5
368
369 #define SHUTTER_INTENSITY 50
370
371 #define SUB_FRAME (p_sys->i_cur_time % (TIME_UNIT_PER_S / SHUTTER_FREQ))
372
373    /*
374     * depending on current time: define shutter location on picture
375     */
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);
380
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);
386
387     int32_t i_width = p_pic_out->p[Y_PLANE].i_visible_pitch
388                     / p_pic_out->p[Y_PLANE].i_pixel_pitch;
389
390    /*
391     * darken pixels currently occulted by shutter
392     */
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] );
396 }
397
398 /**
399  * sliding & offset effect
400  */
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;
403
404
405    /**
406     * one shot offset section
407     */
408
409 #define OFFSET_AVERAGE_PERIOD   (10 * TIME_UNIT_PER_S)
410
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;
426
427
428     /**
429     * sliding section
430     */
431
432 #define SLIDING_AVERAGE_PERIOD   (20 * TIME_UNIT_PER_S)
433 #define SLIDING_AVERAGE_DURATION ( 3 * TIME_UNIT_PER_S)
434
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;
443     }
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;
456     }
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 ) ) {
460
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;
464
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 ) {
470
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;
474         }
475     }
476
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;
481
482     p_sys->i_sliding_ofs = MOD( p_sys->i_sliding_ofs,
483                                 p_sys->i_height[Y_PLANE] * 100 );
484
485     /* apply offset */
486     return oldmovie_sliding_offset_apply( p_filter, p_pic_out );
487 }
488
489 /**
490 * apply both sliding and offset effect
491 */
492 static int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out )
493 {
494     filter_sys_t *p_sys = p_filter->p_sys;
495
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 );
504
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];
512
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);
516         }
517         free( p_temp_buf );
518     }
519
520     return VLC_SUCCESS;
521 }
522
523 /**
524  * Black and white transform including a touch of sepia effect
525  */
526 static void oldmovie_black_n_white_effect( picture_t *p_pic_out )
527 {
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 ) {
531
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;
535         }
536
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 );
541 }
542
543 /**
544  * Smooth darker borders effect
545  */
546 static int oldmovie_dark_border_effect( filter_t *p_filter, picture_t *p_pic_out )
547 {
548     filter_sys_t *p_sys = p_filter->p_sys;
549
550 #define BORDER_DIST 20
551
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++ ) {
554
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);
557
558             int32_t i_border_dist = __MAX(BORDER_DIST - i_x_border_dist,0)
559                                   + __MAX(BORDER_DIST - i_y_border_dist,0);
560
561             i_border_dist = __MIN(BORDER_DIST, i_border_dist);
562
563             if ( i_border_dist == 0 )
564                 continue;
565
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;
568
569             SUB_MIN( p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs],
570                      i_border_dist * 255 / BORDER_DIST, 0 );
571         }
572
573     return VLC_SUCCESS;
574 }
575
576 /**
577  * Vertical scratch random management and effect
578  */
579 static int oldmovie_film_scratch_effect( filter_t *p_filter, picture_t *p_pic_out )
580 {
581     filter_sys_t *p_sys = p_filter->p_sys;
582
583 #define SCRATCH_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 2 )
584 #define SCRATCH_DURATION         ( TIME_UNIT_PER_S * 1 / 2)
585
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;
594
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;
606
607                 break;
608             }
609         p_sys->i_scratch_trigger = p_sys->i_cur_time
610                                  + ( (uint64_t)vlc_mrand48() ) % SCRATCH_GENERATOR_PERIOD
611                                  + SCRATCH_GENERATOR_PERIOD / 2;
612     }
613
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;
621             }
622
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] );
631         }
632
633     return VLC_SUCCESS;
634 }
635
636 /**
637  * Blotch addition
638  *    bigger than dust but only during one frame (due to a local film damage)
639  */
640 static void oldmovie_film_blotch_effect( filter_t *p_filter, picture_t *p_pic_out )
641 {
642     filter_sys_t *p_sys = p_filter->p_sys;
643
644 #define BLOTCH_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 5 )
645
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;
653
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] );
668         }
669
670         p_sys->i_blotch_trigger = p_sys->i_cur_time
671                                 + (uint64_t)vlc_mrand48() % BLOTCH_GENERATOR_PERIOD
672                                 + BLOTCH_GENERATOR_PERIOD / 2;
673     }
674 }
675
676 /**
677  * Dust dots addition, visible during one frame only (film damages)
678  */
679 static void oldmovie_film_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
680 #define ONESHOT_DUST_RATIO 1000
681
682     filter_sys_t *p_sys = p_filter->p_sys;
683
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] );
695 }
696
697 /**
698  * Hair and dust on projector lens
699  *
700  */
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   )
705
706 /**
707  * Define hair location on the lens and timeout
708  *
709  */
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;
712
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;
716
717     ps_hair->i_stop_trigger = p_sys->i_cur_time
718                             + (uint64_t)vlc_mrand48() % HAIR_DURATION
719                             + HAIR_DURATION / 2;
720 }
721
722 /**
723  * Show black hair on the screen
724  *       after random duration it is removed or re-located
725  */
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;
728
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;
737
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;
745
746                 oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
747
748                 break;
749             }
750         p_sys->i_hair_trigger = p_sys->i_cur_time
751                               + (uint64_t)vlc_mrand48() % HAIR_GENERATOR_PERIOD
752                               + HAIR_GENERATOR_PERIOD / 2;
753     }
754
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;
768                 }
769             }
770
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;
774
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] );
789                 }
790             }
791         }
792
793     return VLC_SUCCESS;
794 }
795
796 /**
797  * Define dust location on the lens and timeout
798  *
799  */
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;
802
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];
805
806     ps_dust->i_stop_trigger = p_sys->i_cur_time
807                             + (uint64_t)vlc_mrand48() % HAIR_DURATION
808                             + HAIR_DURATION / 2;
809
810
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] );
813
814     ps_dust->i_stop_trigger = p_sys->i_cur_time
815                             + (uint64_t)vlc_mrand48() % DUST_DURATION
816                             + DUST_DURATION / 2;
817 }
818
819 /**
820  * Dust addition
821  *    smaller than blotch but will remain on the screen for long time
822  */
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;
825
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;
834
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;
839
840                 break;
841             }
842         p_sys->i_dust_trigger = p_sys->i_cur_time
843                               + (uint64_t)vlc_mrand48() % DUST_GENERATOR_PERIOD
844                               + DUST_GENERATOR_PERIOD / 2;
845     }
846
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;
860                 }
861             }
862
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] );
871         }
872
873     return VLC_SUCCESS;
874 }