demux: mp4: avoid audio cuts on seek
[vlc.git] / modules / video_filter / oldmovie.c
blobf78e0f37511aec1758a24b45c785cdf707492c21
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_picture.h>
38 #include <vlc_rand.h>
39 #include <vlc_mtime.h>
41 #include "filter_picture.h"
43 static inline int64_t MOD(int64_t a, int64_t b) {
44 return ( ( a % b ) + b ) % b; }
46 #define SUB_MIN(val, sub_val, min) val = \
47 ((val-(int32_t)sub_val)<min?min:val-sub_val)
48 #define ADD_MAX(val, add_val, max) val = \
49 ((val+(int32_t)add_val)>max?max:val+add_val)
51 static inline int32_t PIX_OFS(int32_t i_x, int32_t i_y, plane_t *ps_plane) {
52 return i_x * ps_plane->i_pixel_pitch + i_y * ps_plane->i_pitch; }
54 #define CHECK_PIX_OFS(i_x, i_y, ps_plane) ( \
55 (i_x) >= 0 && (i_y) >= 0 && \
56 (i_x) * ps_plane->i_pixel_pitch < ps_plane->i_visible_pitch && \
57 (i_y) < ps_plane->i_visible_lines \
60 static inline void DARKEN_PIXEL(int32_t i_x, int32_t i_y,
61 int16_t intensity, plane_t *ps_plane) {
62 SUB_MIN( ps_plane->p_pixels[ PIX_OFS(i_x, i_y, ps_plane) ],
63 intensity, 0 );
66 static inline void LIGHTEN_PIXEL(int32_t i_x, int32_t i_y,
67 int16_t intensity, plane_t *ps_plane) {
68 ADD_MAX( ps_plane->p_pixels[ PIX_OFS(i_x, i_y, ps_plane) ],
69 intensity, 0xFF );
72 static inline void CHECK_N_DARKEN_PIXEL(int32_t i_x, int32_t i_y,
73 int16_t intensity, plane_t *ps_plane) {
74 if ( likely( CHECK_PIX_OFS(i_x, i_y, ps_plane) ) )
75 DARKEN_PIXEL(i_x, i_y, intensity, ps_plane);
78 static inline void CHECK_N_LIGHTEN_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 LIGHTEN_PIXEL(i_x, i_y, intensity, ps_plane);
84 #define MAX_SCRATCH 20
85 #define MAX_HAIR 10
86 #define MAX_DUST 10
88 typedef struct {
89 int32_t i_offset;
90 int32_t i_width;
91 uint16_t i_intensity;
92 mtime_t i_stop_trigger;
93 } scratch_t;
95 typedef struct {
96 int32_t i_x, i_y;
97 uint8_t i_rotation;
98 int32_t i_width;
99 int32_t i_length;
100 int32_t i_curve;
101 uint16_t i_intensity;
102 mtime_t i_stop_trigger;
103 } hair_t;
105 typedef struct {
106 int32_t i_x, i_y;
107 int32_t i_width;
108 uint16_t i_intensity;
109 mtime_t i_stop_trigger;
110 } dust_t;
112 struct filter_sys_t {
114 /* general data */
115 bool b_init;
116 int32_t i_planes;
117 int32_t *i_height;
118 int32_t *i_width;
119 int32_t *i_visible_pitch;
120 mtime_t i_start_time;
121 mtime_t i_last_time;
122 mtime_t i_cur_time;
124 /* sliding & offset effect */
125 mtime_t i_offset_trigger;
126 mtime_t i_sliding_trigger;
127 mtime_t i_sliding_stop_trig;
128 int32_t i_offset_ofs;
129 int32_t i_sliding_ofs;
130 int32_t i_sliding_speed;
132 /* scratch on film */
133 mtime_t i_scratch_trigger;
134 scratch_t *p_scratch[MAX_SCRATCH];
136 /* hair on lens */
137 mtime_t i_hair_trigger;
138 hair_t *p_hair[MAX_HAIR];
140 /* blotch on film */
141 mtime_t i_blotch_trigger;
143 /* dust on lens */
144 mtime_t i_dust_trigger;
145 dust_t *p_dust[MAX_DUST];
148 /*****************************************************************************
149 * Prototypes
150 *****************************************************************************/
152 static picture_t *Filter( filter_t *, picture_t * );
154 static int oldmovie_allocate_data( filter_t *, picture_t * );
155 static void oldmovie_free_allocated_data( filter_t * );
157 static void oldmovie_shutter_effect( filter_t *, picture_t * );
158 static int oldmovie_sliding_offset_effect( filter_t *, picture_t * );
159 static void oldmovie_black_n_white_effect( picture_t * );
160 static int oldmovie_dark_border_effect( filter_t *, picture_t * );
161 static int oldmovie_film_scratch_effect( filter_t *, picture_t * );
162 static void oldmovie_film_blotch_effect( filter_t *, picture_t * );
163 static void oldmovie_film_dust_effect( filter_t *, picture_t * );
164 static int oldmovie_lens_hair_effect( filter_t *, picture_t * );
165 static int oldmovie_lens_dust_effect( filter_t *, picture_t * );
167 static void oldmovie_define_hair_location( filter_t *p_filter, hair_t* ps_hair );
168 static void oldmovie_define_dust_location( filter_t *p_filter, dust_t* ps_dust );
169 static int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out );
171 /*****************************************************************************
172 * Module descriptor
173 *****************************************************************************/
175 static int Open ( vlc_object_t * );
176 static void Close( vlc_object_t * );
178 vlc_module_begin()
179 set_description( N_("Old movie effect video filter") )
180 set_shortname( N_( "Old movie" ))
181 set_capability( "video filter", 0 )
182 set_category( CAT_VIDEO )
183 set_subcategory( SUBCAT_VIDEO_VFILTER )
185 set_callbacks( Open, Close )
186 vlc_module_end()
189 * Open the filter
191 static int Open( vlc_object_t *p_this ) {
192 filter_t *p_filter = (filter_t *)p_this;
193 filter_sys_t *p_sys;
195 /* Assert video in match with video out */
196 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
197 msg_Err( p_filter, "Input and output format does not match" );
198 return VLC_EGENERIC;
201 /* Reject 0 bpp and unsupported chroma */
202 const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
203 const vlc_chroma_description_t *p_chroma =
204 vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
205 if( !p_chroma || p_chroma->pixel_size == 0
206 || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
207 || !vlc_fourcc_IsYUV( fourcc ) ) {
209 msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
210 return VLC_EGENERIC;
213 /* Allocate structure */
214 p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
215 if( unlikely( !p_sys ) )
216 return VLC_ENOMEM;
218 /* init data */
219 p_filter->pf_video_filter = Filter;
220 p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = mdate();
222 return VLC_SUCCESS;
226 * Close the filter
228 static void Close( vlc_object_t *p_this ) {
229 filter_t *p_filter = (filter_t *)p_this;
230 filter_sys_t *p_sys = p_filter->p_sys;
232 /* Free allocated memory */
233 oldmovie_free_allocated_data( p_filter );
234 free( p_sys );
238 * Filter a picture
240 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
241 if( unlikely( !p_pic_in || !p_filter ) )
242 return NULL;
244 filter_sys_t *p_sys = p_filter->p_sys;
246 picture_t *p_pic_out = filter_NewPicture( p_filter );
247 if( unlikely( !p_pic_out ) ) {
248 picture_Release( p_pic_in );
249 return NULL;
253 * manage time
255 p_sys->i_last_time = p_sys->i_cur_time;
256 p_sys->i_cur_time = mdate();
259 * allocate data
261 if ( unlikely( !p_sys->b_init ) )
262 if ( unlikely( oldmovie_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
263 picture_Release( p_pic_in );
264 return NULL;
266 p_sys->b_init = true;
269 * preset output pic: raw copy src to dst
271 picture_CopyPixels(p_pic_out, p_pic_in);
274 * apply several effects on picture
276 oldmovie_black_n_white_effect( p_pic_out );
278 /* simulates projector shutter blinking effect */
279 oldmovie_shutter_effect(p_filter, p_pic_out);
281 if ( unlikely( oldmovie_sliding_offset_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
282 return CopyInfoAndRelease( p_pic_out, p_pic_in );
284 oldmovie_dark_border_effect( p_filter, p_pic_out );
286 if ( unlikely( oldmovie_film_scratch_effect(p_filter, p_pic_out) != VLC_SUCCESS ) )
287 return CopyInfoAndRelease( p_pic_out, p_pic_in );
289 oldmovie_film_blotch_effect(p_filter, p_pic_out);
291 if ( unlikely( oldmovie_lens_hair_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
292 return CopyInfoAndRelease( p_pic_out, p_pic_in );
294 if ( unlikely( oldmovie_lens_dust_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
295 return CopyInfoAndRelease( p_pic_out, p_pic_in );
297 oldmovie_film_dust_effect( p_filter, p_pic_out );
299 return CopyInfoAndRelease( p_pic_out, p_pic_in );
303 * Allocate data
305 static int oldmovie_allocate_data( filter_t *p_filter, picture_t *p_pic_in ) {
306 filter_sys_t *p_sys = p_filter->p_sys;
308 oldmovie_free_allocated_data( p_filter );
311 * take into account different characteristics for each plane
313 p_sys->i_planes = p_pic_in->i_planes;
314 p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
315 p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
316 p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
318 if( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) ) {
319 oldmovie_free_allocated_data( p_filter );
320 return VLC_ENOMEM;
323 for (int32_t i_p=0; i_p < p_sys->i_planes; i_p++) {
324 p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
325 p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
326 p_sys->i_width [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch
327 / p_pic_in->p[i_p].i_pixel_pitch;
329 return VLC_SUCCESS;
333 * Free allocated data
335 static void oldmovie_free_allocated_data( filter_t *p_filter ) {
336 filter_sys_t *p_sys = p_filter->p_sys;
338 for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
339 FREENULL(p_sys->p_scratch[i_s]);
341 for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
342 FREENULL(p_sys->p_hair[i_h]);
344 for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
345 FREENULL(p_sys->p_dust[i_d]);
347 p_sys->i_planes = 0;
348 FREENULL( p_sys->i_height );
349 FREENULL( p_sys->i_width );
350 FREENULL( p_sys->i_visible_pitch );
354 * Projector shutter effect
356 static void oldmovie_shutter_effect( filter_t *p_filter, picture_t *p_pic_out ) {
357 filter_sys_t *p_sys = p_filter->p_sys;
359 #define SHUTTER_FREQ 2
360 #define SHUTTER_SPEED 25
361 #define SHUTTER_HEIGHT 1.5
363 #define SHUTTER_INTENSITY 50
365 #define SUB_FRAME (p_sys->i_cur_time % (CLOCK_FREQ / SHUTTER_FREQ))
368 * depending on current time: define shutter location on picture
370 int32_t i_shutter_sup = VLC_CLIP((int64_t)SUB_FRAME
371 * p_pic_out->p[Y_PLANE].i_visible_lines
372 * SHUTTER_SPEED / CLOCK_FREQ,
373 0, p_pic_out->p[Y_PLANE].i_visible_lines);
375 int32_t i_shutter_inf = VLC_CLIP((int64_t)SUB_FRAME
376 * p_pic_out->p[Y_PLANE].i_visible_lines
377 * SHUTTER_SPEED / CLOCK_FREQ
378 - SHUTTER_HEIGHT * p_pic_out->p[Y_PLANE].i_visible_lines,
379 0, p_pic_out->p[Y_PLANE].i_visible_lines);
381 int32_t i_width = p_pic_out->p[Y_PLANE].i_visible_pitch
382 / p_pic_out->p[Y_PLANE].i_pixel_pitch;
385 * darken pixels currently occulted by shutter
387 for ( int32_t i_y = i_shutter_inf; i_y < i_shutter_sup; i_y++ )
388 for ( int32_t i_x = 0; i_x < i_width; i_x++ )
389 DARKEN_PIXEL( i_x, i_y, SHUTTER_INTENSITY, &p_pic_out->p[Y_PLANE] );
393 * sliding & offset effect
395 static int oldmovie_sliding_offset_effect( filter_t *p_filter, picture_t *p_pic_out ) {
396 filter_sys_t *p_sys = p_filter->p_sys;
400 * one shot offset section
403 #define OFFSET_AVERAGE_PERIOD (10 * CLOCK_FREQ)
405 /* start trigger to be (re)initialized */
406 if ( p_sys->i_offset_trigger == 0
407 || p_sys->i_sliding_speed != 0 ) { /* do not mix sliding and offset */
408 /* random trigger for offset effect */
409 p_sys->i_offset_trigger = p_sys->i_cur_time
410 + ( (uint64_t) vlc_mrand48() ) % OFFSET_AVERAGE_PERIOD
411 + OFFSET_AVERAGE_PERIOD / 2;
412 p_sys->i_offset_ofs = 0;
413 } else if ( p_sys->i_offset_trigger <= p_sys->i_cur_time ) {
414 /* trigger for offset effect */
415 p_sys->i_offset_trigger = 0;
416 p_sys->i_offset_ofs = MOD( ( (uint32_t)vlc_mrand48() ),
417 p_sys->i_height[Y_PLANE] ) * 100;
418 } else
419 p_sys->i_offset_ofs = 0;
423 * sliding section
426 #define SLIDING_AVERAGE_PERIOD (20 * CLOCK_FREQ)
427 #define SLIDING_AVERAGE_DURATION ( 3 * CLOCK_FREQ)
429 /* start trigger to be (re)initialized */
430 if ( ( p_sys->i_sliding_stop_trig == 0 )
431 && ( p_sys->i_sliding_trigger == 0 )
432 && ( p_sys->i_sliding_speed == 0 ) ) {
433 /* random trigger which enable sliding effect */
434 p_sys->i_sliding_trigger = p_sys->i_cur_time
435 + ( (uint64_t) vlc_mrand48() ) % SLIDING_AVERAGE_PERIOD
436 + SLIDING_AVERAGE_PERIOD / 2;
438 /* start trigger just occurs */
439 else if ( ( p_sys->i_sliding_stop_trig == 0 )
440 && ( p_sys->i_sliding_trigger <= p_sys->i_cur_time )
441 && ( p_sys->i_sliding_speed == 0 ) ) {
442 /* init sliding parameters */
443 p_sys->i_sliding_trigger = 0;
444 p_sys->i_sliding_stop_trig = p_sys->i_cur_time
445 + ((uint64_t) vlc_mrand48() ) % SLIDING_AVERAGE_DURATION
446 + SLIDING_AVERAGE_DURATION / 2;
447 p_sys->i_sliding_ofs = 0;
448 /* note: sliding speed unit = image per 100 s */
449 p_sys->i_sliding_speed = MOD(((int32_t) vlc_mrand48() ), 201) - 100;
451 /* stop trigger disabling sliding effect */
452 else if ( ( p_sys->i_sliding_stop_trig <= p_sys->i_cur_time )
453 && ( p_sys->i_sliding_trigger == 0 ) ) {
455 /* first increase speed to ensure we will come back to stable image */
456 if ( abs(p_sys->i_sliding_speed) < 50 )
457 p_sys->i_sliding_speed += 5;
459 /* check if offset is close to 0 and then ready to stop */
460 if ( abs( p_sys->i_sliding_ofs ) < abs( p_sys->i_sliding_speed
461 * p_sys->i_height[Y_PLANE]
462 * ( p_sys->i_cur_time - p_sys->i_last_time ) / CLOCK_FREQ )
463 || abs( p_sys->i_sliding_ofs ) < p_sys->i_height[Y_PLANE] * 100 / 20 ) {
465 /* reset sliding parameters */
466 p_sys->i_sliding_ofs = p_sys->i_sliding_speed = 0;
467 p_sys->i_sliding_trigger = p_sys->i_sliding_stop_trig = 0;
471 /* update offset */
472 p_sys->i_sliding_ofs += p_sys->i_sliding_speed * p_sys->i_height[Y_PLANE]
473 * ( p_sys->i_cur_time - p_sys->i_last_time )
474 / CLOCK_FREQ;
476 p_sys->i_sliding_ofs = MOD( p_sys->i_sliding_ofs,
477 p_sys->i_height[Y_PLANE] * 100 );
479 /* apply offset */
480 return oldmovie_sliding_offset_apply( p_filter, p_pic_out );
484 * apply both sliding and offset effect
486 static int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out )
488 filter_sys_t *p_sys = p_filter->p_sys;
490 for ( uint8_t i_p = 0; i_p < p_pic_out->i_planes; i_p++ ) {
491 /* first allocate temporary buffer for swap operation */
492 uint8_t *p_temp_buf = calloc( p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch,
493 sizeof(uint8_t) );
494 if ( unlikely( !p_temp_buf ) )
495 return VLC_ENOMEM;
496 memcpy( p_temp_buf,p_pic_out->p[i_p].p_pixels,
497 p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch );
499 /* copy lines to output_pic */
500 for ( int32_t i_y = 0; i_y < p_pic_out->p[i_p].i_visible_lines; i_y++ ) {
501 int32_t i_ofs = MOD( ( p_sys->i_offset_ofs + p_sys->i_sliding_ofs )
502 /100,
503 p_sys->i_height[Y_PLANE] );
504 i_ofs *= p_pic_out->p[i_p].i_visible_lines;
505 i_ofs /= p_sys->i_height[Y_PLANE];
507 memcpy( &p_pic_out->p[i_p].p_pixels[ i_y * p_pic_out->p[i_p].i_pitch ],
508 &p_temp_buf[ ( ( i_y + i_ofs ) % p_pic_out->p[i_p].i_visible_lines ) * p_pic_out->p[i_p].i_pitch ],
509 p_pic_out->p[i_p].i_visible_pitch);
511 free( p_temp_buf );
514 return VLC_SUCCESS;
518 * Black and white transform including a touch of sepia effect
520 static void oldmovie_black_n_white_effect( picture_t *p_pic_out )
522 for ( int32_t i_y = 0; i_y < p_pic_out->p[Y_PLANE].i_visible_lines; i_y++ )
523 for ( int32_t i_x = 0; i_x < p_pic_out->p[Y_PLANE].i_visible_pitch;
524 i_x += p_pic_out->p[Y_PLANE].i_pixel_pitch ) {
526 uint32_t i_pix_ofs = i_x+i_y*p_pic_out->p[Y_PLANE].i_pitch;
527 p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] -= p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] >> 2;
528 p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] += 30;
531 memset( p_pic_out->p[U_PLANE].p_pixels, 122,
532 p_pic_out->p[U_PLANE].i_lines * p_pic_out->p[U_PLANE].i_pitch );
533 memset( p_pic_out->p[V_PLANE].p_pixels, 132,
534 p_pic_out->p[V_PLANE].i_lines * p_pic_out->p[V_PLANE].i_pitch );
538 * Smooth darker borders effect
540 static int oldmovie_dark_border_effect( filter_t *p_filter, picture_t *p_pic_out )
542 filter_sys_t *p_sys = p_filter->p_sys;
544 #define BORDER_DIST 20
546 for ( int32_t i_y = 0; i_y < p_sys->i_height[Y_PLANE]; i_y++ )
547 for ( int32_t i_x = 0; i_x < p_sys->i_width[Y_PLANE]; i_x++ ) {
549 int32_t i_x_border_dist = __MIN( i_x, p_sys->i_width[Y_PLANE] - i_x);
550 int32_t i_y_border_dist = __MIN( i_y, p_sys->i_height[Y_PLANE] - i_y);
552 int32_t i_border_dist = __MAX(BORDER_DIST - i_x_border_dist,0)
553 + __MAX(BORDER_DIST - i_y_border_dist,0);
555 i_border_dist = __MIN(BORDER_DIST, i_border_dist);
557 if ( i_border_dist == 0 )
558 continue;
560 uint32_t i_pix_ofs = i_x * p_pic_out->p[Y_PLANE].i_pixel_pitch
561 + i_y * p_pic_out->p[Y_PLANE].i_pitch;
563 SUB_MIN( p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs],
564 i_border_dist * 255 / BORDER_DIST, 0 );
567 return VLC_SUCCESS;
571 * Vertical scratch random management and effect
573 static int oldmovie_film_scratch_effect( filter_t *p_filter, picture_t *p_pic_out )
575 filter_sys_t *p_sys = p_filter->p_sys;
577 #define SCRATCH_GENERATOR_PERIOD ( CLOCK_FREQ * 2 )
578 #define SCRATCH_DURATION ( CLOCK_FREQ * 1 / 2)
580 /* generate new scratch */
581 if ( p_sys->i_scratch_trigger <= p_sys->i_cur_time ) {
582 for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
583 if ( p_sys->p_scratch[i_s] == NULL ) {
584 /* allocate data */
585 p_sys->p_scratch[i_s] = calloc( 1, sizeof(scratch_t) );
586 if ( unlikely( !p_sys->p_scratch[i_s] ) )
587 return VLC_ENOMEM;
589 /* set random parameters */
590 p_sys->p_scratch[i_s]->i_offset = ( ( (unsigned)vlc_mrand48() )
591 % __MAX( p_sys->i_width[Y_PLANE] - 10, 1 ) )
592 + 5;
593 p_sys->p_scratch[i_s]->i_width = ( ( (unsigned)vlc_mrand48() )
594 % __MAX( p_sys->i_width[Y_PLANE] / 500, 1 ) )
595 + 1;
596 p_sys->p_scratch[i_s]->i_intensity = (unsigned) vlc_mrand48() % 50 + 10;
597 p_sys->p_scratch[i_s]->i_stop_trigger = p_sys->i_cur_time
598 + (uint64_t) vlc_mrand48() % SCRATCH_DURATION
599 + SCRATCH_DURATION / 2;
601 break;
603 p_sys->i_scratch_trigger = p_sys->i_cur_time
604 + ( (uint64_t)vlc_mrand48() ) % SCRATCH_GENERATOR_PERIOD
605 + SCRATCH_GENERATOR_PERIOD / 2;
608 /* manage and apply current scratch */
609 for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
610 if ( p_sys->p_scratch[i_s] ) {
611 /* remove outdated scratch */
612 if ( p_sys->p_scratch[i_s]->i_stop_trigger <= p_sys->i_cur_time ) {
613 FREENULL( p_sys->p_scratch[i_s] );
614 continue;
617 /* otherwise apply scratch */
618 for ( int32_t i_y = 0; i_y < p_pic_out->p[Y_PLANE].i_visible_lines; i_y++ )
619 for ( int32_t i_x = p_sys->p_scratch[i_s]->i_offset;
620 i_x < __MIN(p_sys->p_scratch[i_s]->i_offset
621 + p_sys->p_scratch[i_s]->i_width, p_sys->i_width[Y_PLANE] );
622 i_x++ )
623 DARKEN_PIXEL( i_x, i_y, p_sys->p_scratch[i_s]->i_intensity,
624 &p_pic_out->p[Y_PLANE] );
627 return VLC_SUCCESS;
631 * Blotch addition
632 * bigger than dust but only during one frame (due to a local film damage)
634 static void oldmovie_film_blotch_effect( filter_t *p_filter, picture_t *p_pic_out )
636 filter_sys_t *p_sys = p_filter->p_sys;
638 #define BLOTCH_GENERATOR_PERIOD ( CLOCK_FREQ * 5 )
640 /* generate blotch */
641 if ( p_sys->i_blotch_trigger <= p_sys->i_cur_time ) {
642 /* set random parameters */
643 int32_t i_bx = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
644 int32_t i_by = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
645 int32_t i_width = (unsigned)vlc_mrand48() % __MAX( 1, p_sys->i_width[Y_PLANE] / 10 ) + 1;
646 int32_t i_intensity = (unsigned)vlc_mrand48() % 50 + 20;
648 if ( (unsigned)vlc_mrand48() & 0x01 ) {
649 /* draw dark blotch */
650 for ( int32_t i_y = -i_width + 1; i_y < i_width; i_y++ )
651 for ( int32_t i_x = -i_width + 1; i_x < i_width; i_x++ )
652 if ( i_x * i_x + i_y * i_y <= i_width * i_width )
653 CHECK_N_DARKEN_PIXEL( i_x + i_bx, i_y + i_by,
654 i_intensity, &p_pic_out->p[Y_PLANE] );
655 } else {
656 /* draw light blotch */
657 for ( int32_t i_y = -i_width+1; i_y < i_width; i_y++ )
658 for ( int32_t i_x = -i_width+1; i_x < i_width; i_x++ )
659 if ( i_x * i_x + i_y * i_y <= i_width * i_width )
660 CHECK_N_LIGHTEN_PIXEL( i_x + i_bx, i_y + i_by,
661 i_intensity, &p_pic_out->p[Y_PLANE] );
664 p_sys->i_blotch_trigger = p_sys->i_cur_time
665 + (uint64_t)vlc_mrand48() % BLOTCH_GENERATOR_PERIOD
666 + BLOTCH_GENERATOR_PERIOD / 2;
671 * Dust dots addition, visible during one frame only (film damages)
673 static void oldmovie_film_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
674 #define ONESHOT_DUST_RATIO 1000
676 filter_sys_t *p_sys = p_filter->p_sys;
678 for ( int32_t i_dust = 0;
679 i_dust < p_sys->i_width[Y_PLANE] * p_sys->i_height[Y_PLANE] / ONESHOT_DUST_RATIO;
680 i_dust++)
681 if ( (unsigned)vlc_mrand48() % 5 < 3 )
682 DARKEN_PIXEL( (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE],
683 (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE],
684 150, &p_pic_out->p[Y_PLANE] );
685 else
686 LIGHTEN_PIXEL( (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE],
687 (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE],
688 50, &p_pic_out->p[Y_PLANE] );
692 * Hair and dust on projector lens
695 #define HAIR_GENERATOR_PERIOD ( CLOCK_FREQ * 50 )
696 #define HAIR_DURATION ( CLOCK_FREQ * 50 )
697 #define DUST_GENERATOR_PERIOD ( CLOCK_FREQ * 100 )
698 #define DUST_DURATION ( CLOCK_FREQ * 4 )
701 * Define hair location on the lens and timeout
704 static void oldmovie_define_hair_location( filter_t *p_filter, hair_t* ps_hair ) {
705 filter_sys_t *p_sys = p_filter->p_sys;
707 ps_hair->i_x = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
708 ps_hair->i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
709 ps_hair->i_rotation = (unsigned)vlc_mrand48() % 200;
711 ps_hair->i_stop_trigger = p_sys->i_cur_time
712 + (uint64_t)vlc_mrand48() % HAIR_DURATION
713 + HAIR_DURATION / 2;
717 * Show black hair on the screen
718 * after random duration it is removed or re-located
720 static int oldmovie_lens_hair_effect( filter_t *p_filter, picture_t *p_pic_out ) {
721 filter_sys_t *p_sys = p_filter->p_sys;
723 /* generate new hair */
724 if ( p_sys->i_hair_trigger <= p_sys->i_cur_time ) {
725 for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
726 if ( p_sys->p_hair[i_h] == NULL ) {
727 /* allocate data */
728 p_sys->p_hair[i_h] = calloc( 1, sizeof(hair_t) );
729 if ( unlikely( !p_sys->p_hair[i_h] ) )
730 return VLC_ENOMEM;
732 /* set random parameters */
733 p_sys->p_hair[i_h]->i_length = (unsigned)vlc_mrand48()
734 % ( p_sys->i_width[Y_PLANE] / 3 ) + 5;
735 p_sys->p_hair[i_h]->i_curve = MOD( (int32_t)vlc_mrand48(), 80 ) - 40;
736 p_sys->p_hair[i_h]->i_width = (unsigned)vlc_mrand48()
737 % __MAX( 1, p_sys->i_width[Y_PLANE] / 1500 ) + 1;
738 p_sys->p_hair[i_h]->i_intensity = (unsigned)vlc_mrand48() % 50 + 20;
740 oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
742 break;
744 p_sys->i_hair_trigger = p_sys->i_cur_time
745 + (uint64_t)vlc_mrand48() % HAIR_GENERATOR_PERIOD
746 + HAIR_GENERATOR_PERIOD / 2;
749 /* manage and apply current hair */
750 for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
751 if ( p_sys->p_hair[i_h] ) {
752 /* remove outdated ones */
753 if ( p_sys->p_hair[i_h]->i_stop_trigger <= p_sys->i_cur_time ) {
754 /* select between moving or removing hair */
755 if ( (unsigned)vlc_mrand48() % 2 == 0 )
756 /* move hair */
757 oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
758 else {
759 /* remove hair */
760 FREENULL( p_sys->p_hair[i_h] );
761 continue;
765 /* draw hair */
766 double f_base_x = (double)p_sys->p_hair[i_h]->i_x;
767 double f_base_y = (double)p_sys->p_hair[i_h]->i_y;
769 for ( int32_t i_l = 0; i_l < p_sys->p_hair[i_h]->i_length; i_l++ ) {
770 uint32_t i_current_rot = p_sys->p_hair[i_h]->i_rotation
771 + p_sys->p_hair[i_h]->i_curve * i_l / 100;
772 f_base_x += cos( (double)i_current_rot / 128.0 * M_PI );
773 f_base_y += sin( (double)i_current_rot / 128.0 * M_PI );
774 double f_current_x = f_base_x;
775 double f_current_y = f_base_y;
776 for ( int32_t i_w = 0; i_w < p_sys->p_hair[i_h]->i_width; i_w++ ) {
777 f_current_x += sin( (double)i_current_rot / 128.0 * M_PI );
778 f_current_y += cos( (double)i_current_rot / 128.0 * M_PI );
779 CHECK_N_DARKEN_PIXEL( (int32_t) f_current_x,
780 (int32_t) f_current_y,
781 p_sys->p_hair[i_h]->i_intensity,
782 &p_pic_out->p[Y_PLANE] );
787 return VLC_SUCCESS;
791 * Define dust location on the lens and timeout
794 static void oldmovie_define_dust_location( filter_t *p_filter, dust_t* ps_dust ) {
795 filter_sys_t *p_sys = p_filter->p_sys;
797 ps_dust->i_x = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
798 ps_dust->i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
800 ps_dust->i_stop_trigger = p_sys->i_cur_time
801 + (uint64_t)vlc_mrand48() % HAIR_DURATION
802 + HAIR_DURATION / 2;
805 ps_dust->i_x = MOD( (int32_t)vlc_mrand48(), p_sys->i_width[Y_PLANE] );
806 ps_dust->i_y = MOD( (int32_t)vlc_mrand48(), p_sys->i_height[Y_PLANE] );
808 ps_dust->i_stop_trigger = p_sys->i_cur_time
809 + (uint64_t)vlc_mrand48() % DUST_DURATION
810 + DUST_DURATION / 2;
814 * Dust addition
815 * smaller than blotch but will remain on the screen for long time
817 static int oldmovie_lens_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
818 filter_sys_t *p_sys = p_filter->p_sys;
820 /* generate new dust */
821 if ( p_sys->i_dust_trigger <= p_sys->i_cur_time ) {
822 for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
823 if ( p_sys->p_dust[i_d] == NULL ) {
824 /* allocate data */
825 p_sys->p_dust[i_d] = calloc( 1, sizeof(dust_t) );
826 if ( unlikely( !p_sys->p_dust[i_d] ) )
827 return VLC_ENOMEM;
829 /* set random parameters */
830 oldmovie_define_dust_location( p_filter, p_sys->p_dust[i_d] );
831 p_sys->p_dust[i_d]->i_width = MOD( (int32_t)vlc_mrand48(), 5 ) + 1;
832 p_sys->p_dust[i_d]->i_intensity = (unsigned)vlc_mrand48() % 30 + 30;
834 break;
836 p_sys->i_dust_trigger = p_sys->i_cur_time
837 + (uint64_t)vlc_mrand48() % DUST_GENERATOR_PERIOD
838 + DUST_GENERATOR_PERIOD / 2;
841 /* manage and apply current dust */
842 for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
843 if ( p_sys->p_dust[i_d] ) {
844 /* remove outdated ones */
845 if ( p_sys->p_dust[i_d]->i_stop_trigger <= p_sys->i_cur_time ) {
846 /* select between moving or removing dust */
847 if ( (unsigned)vlc_mrand48() % 2 == 0 )
848 /* move dust */
849 oldmovie_define_dust_location( p_filter, p_sys->p_dust[i_d] );
850 else {
851 /* remove dust */
852 FREENULL( p_sys->p_dust[i_d] );
853 continue;
857 /* draw dust */
858 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++ )
859 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++ )
860 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 )
861 CHECK_N_DARKEN_PIXEL( i_x + p_sys->p_dust[i_d]->i_x,
862 i_y + p_sys->p_dust[i_d]->i_y,
863 p_sys->p_dust[i_d]->i_intensity,
864 &p_pic_out->p[Y_PLANE] );
867 return VLC_SUCCESS;