1 /*****************************************************************************
2 * oldmovie.c : Old movie effect video filter
3 *****************************************************************************
4 * Copyright (C) 2013 Vianney Boyer
6 * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
36 #include <vlc_picture.h>
40 #include "filter_picture.h"
42 static inline int64_t MOD(int64_t a
, int64_t b
) {
43 return ( ( a
% b
) + b
) % b
; }
45 #define SUB_MIN(val, sub_val, min) val = \
46 ((val-(int32_t)sub_val)<min?min:val-sub_val)
47 #define ADD_MAX(val, add_val, max) val = \
48 ((val+(int32_t)add_val)>max?max:val+add_val)
50 static inline int32_t PIX_OFS(int32_t i_x
, int32_t i_y
, plane_t
*ps_plane
) {
51 return i_x
* ps_plane
->i_pixel_pitch
+ i_y
* ps_plane
->i_pitch
; }
53 #define CHECK_PIX_OFS(i_x, i_y, ps_plane) ( \
54 (i_x) >= 0 && (i_y) >= 0 && \
55 (i_x) * ps_plane->i_pixel_pitch < ps_plane->i_visible_pitch && \
56 (i_y) < ps_plane->i_visible_lines \
59 static inline void DARKEN_PIXEL(int32_t i_x
, int32_t i_y
,
60 int16_t intensity
, plane_t
*ps_plane
) {
61 SUB_MIN( ps_plane
->p_pixels
[ PIX_OFS(i_x
, i_y
, ps_plane
) ],
65 static inline void LIGHTEN_PIXEL(int32_t i_x
, int32_t i_y
,
66 int16_t intensity
, plane_t
*ps_plane
) {
67 ADD_MAX( ps_plane
->p_pixels
[ PIX_OFS(i_x
, i_y
, ps_plane
) ],
71 static inline void CHECK_N_DARKEN_PIXEL(int32_t i_x
, int32_t i_y
,
72 int16_t intensity
, plane_t
*ps_plane
) {
73 if ( likely( CHECK_PIX_OFS(i_x
, i_y
, ps_plane
) ) )
74 DARKEN_PIXEL(i_x
, i_y
, intensity
, ps_plane
);
77 static inline void CHECK_N_LIGHTEN_PIXEL(int32_t i_x
, int32_t i_y
,
78 int16_t intensity
, plane_t
*ps_plane
) {
79 if ( likely( CHECK_PIX_OFS(i_x
, i_y
, ps_plane
) ) )
80 LIGHTEN_PIXEL(i_x
, i_y
, intensity
, ps_plane
);
83 #define MAX_SCRATCH 20
91 vlc_tick_t i_stop_trigger
;
100 uint16_t i_intensity
;
101 vlc_tick_t i_stop_trigger
;
107 uint16_t i_intensity
;
108 vlc_tick_t i_stop_trigger
;
119 int32_t *i_visible_pitch
;
120 vlc_tick_t i_start_time
;
121 vlc_tick_t i_last_time
;
122 vlc_tick_t i_cur_time
;
124 /* sliding & offset effect */
125 vlc_tick_t i_offset_trigger
;
126 vlc_tick_t i_sliding_trigger
;
127 vlc_tick_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 vlc_tick_t i_scratch_trigger
;
134 scratch_t
*p_scratch
[MAX_SCRATCH
];
137 vlc_tick_t i_hair_trigger
;
138 hair_t
*p_hair
[MAX_HAIR
];
141 vlc_tick_t i_blotch_trigger
;
144 vlc_tick_t i_dust_trigger
;
145 dust_t
*p_dust
[MAX_DUST
];
148 /*****************************************************************************
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 /*****************************************************************************
173 *****************************************************************************/
175 static int Open ( vlc_object_t
* );
176 static void Close( vlc_object_t
* );
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
)
191 static int Open( vlc_object_t
*p_this
) {
192 filter_t
*p_filter
= (filter_t
*)p_this
;
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" );
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
);
213 /* Allocate structure */
214 p_filter
->p_sys
= p_sys
= calloc( 1, sizeof(*p_sys
) );
215 if( unlikely( !p_sys
) )
219 p_filter
->pf_video_filter
= Filter
;
220 p_sys
->i_start_time
= p_sys
->i_cur_time
= p_sys
->i_last_time
= vlc_tick_now();
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
);
240 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic_in
) {
241 if( unlikely( !p_pic_in
|| !p_filter
) )
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
);
255 p_sys
->i_last_time
= p_sys
->i_cur_time
;
256 p_sys
->i_cur_time
= vlc_tick_now();
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
);
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
);
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
);
323 for (size_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
;
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 ( size_t i_s
= 0; i_s
< MAX_SCRATCH
; i_s
++ )
339 FREENULL(p_sys
->p_scratch
[i_s
]);
341 for ( size_t i_h
= 0; i_h
< MAX_HAIR
; i_h
++ )
342 FREENULL(p_sys
->p_hair
[i_h
]);
344 for ( size_t i_d
= 0; i_d
< MAX_DUST
; i_d
++ )
345 FREENULL(p_sys
->p_dust
[i_d
]);
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
] );
392 static vlc_tick_t
RandomEnd(filter_sys_t
*p_sys
, vlc_tick_t modulo
)
394 return p_sys
->i_cur_time
+ (uint64_t)vlc_mrand48() % modulo
+ modulo
/ 2;
398 * sliding & offset effect
400 static int oldmovie_sliding_offset_effect( filter_t
*p_filter
, picture_t
*p_pic_out
) {
401 filter_sys_t
*p_sys
= p_filter
->p_sys
;
405 * one shot offset section
408 #define OFFSET_AVERAGE_PERIOD VLC_TICK_FROM_SEC(10)
410 /* start trigger to be (re)initialized */
411 if ( p_sys
->i_offset_trigger
== 0
412 || p_sys
->i_sliding_speed
!= 0 ) { /* do not mix sliding and offset */
413 /* random trigger for offset effect */
414 p_sys
->i_offset_trigger
= RandomEnd(p_sys
, OFFSET_AVERAGE_PERIOD
);
415 p_sys
->i_offset_ofs
= 0;
416 } else if ( p_sys
->i_offset_trigger
<= p_sys
->i_cur_time
) {
417 /* trigger for offset effect */
418 p_sys
->i_offset_trigger
= 0;
419 p_sys
->i_offset_ofs
= MOD( ( (uint32_t)vlc_mrand48() ),
420 p_sys
->i_height
[Y_PLANE
] ) * 100;
422 p_sys
->i_offset_ofs
= 0;
429 #define SLIDING_AVERAGE_PERIOD VLC_TICK_FROM_SEC(20)
430 #define SLIDING_AVERAGE_DURATION VLC_TICK_FROM_SEC(3)
432 /* start trigger to be (re)initialized */
433 if ( ( p_sys
->i_sliding_stop_trig
== 0 )
434 && ( p_sys
->i_sliding_trigger
== 0 )
435 && ( p_sys
->i_sliding_speed
== 0 ) ) {
436 /* random trigger which enable sliding effect */
437 p_sys
->i_sliding_trigger
= RandomEnd(p_sys
, SLIDING_AVERAGE_PERIOD
);
439 /* start trigger just occurs */
440 else if ( ( p_sys
->i_sliding_stop_trig
== 0 )
441 && ( p_sys
->i_sliding_trigger
<= p_sys
->i_cur_time
)
442 && ( p_sys
->i_sliding_speed
== 0 ) ) {
443 /* init sliding parameters */
444 p_sys
->i_sliding_trigger
= 0;
445 p_sys
->i_sliding_stop_trig
= RandomEnd(p_sys
, SLIDING_AVERAGE_DURATION
);
446 p_sys
->i_sliding_ofs
= 0;
447 /* note: sliding speed unit = image per 100 s */
448 p_sys
->i_sliding_speed
= MOD(((int32_t) vlc_mrand48() ), 201) - 100;
450 /* stop trigger disabling sliding effect */
451 else if ( ( p_sys
->i_sliding_stop_trig
<= p_sys
->i_cur_time
)
452 && ( p_sys
->i_sliding_trigger
== 0 ) ) {
454 /* first increase speed to ensure we will come back to stable image */
455 if ( abs(p_sys
->i_sliding_speed
) < 50 )
456 p_sys
->i_sliding_speed
+= 5;
458 long long i_position
= p_sys
->i_sliding_speed
459 * p_sys
->i_height
[Y_PLANE
]
460 * SEC_FROM_VLC_TICK( p_sys
->i_cur_time
- p_sys
->i_last_time
);
462 /* check if offset is close to 0 and then ready to stop */
463 if ( abs( p_sys
->i_sliding_ofs
) < llabs( i_position
)
464 || abs( p_sys
->i_sliding_ofs
) < p_sys
->i_height
[Y_PLANE
] * 100 / 20 ) {
466 /* reset sliding parameters */
467 p_sys
->i_sliding_ofs
= p_sys
->i_sliding_speed
= 0;
468 p_sys
->i_sliding_trigger
= p_sys
->i_sliding_stop_trig
= 0;
473 p_sys
->i_sliding_ofs
+= p_sys
->i_sliding_speed
* p_sys
->i_height
[Y_PLANE
]
474 * SEC_FROM_VLC_TICK( p_sys
->i_cur_time
- p_sys
->i_last_time
);
476 p_sys
->i_sliding_ofs
= MOD( p_sys
->i_sliding_ofs
,
477 p_sys
->i_height
[Y_PLANE
] * 100 );
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 assert(p_pic_out
->i_planes
> 0);
491 size_t i_planes
= p_pic_out
->i_planes
;
493 for ( size_t i_p
= 0; i_p
< i_planes
; i_p
++ ) {
494 /* first allocate temporary buffer for swap operation */
495 uint8_t *p_temp_buf
= calloc( p_pic_out
->p
[i_p
].i_lines
* p_pic_out
->p
[i_p
].i_pitch
,
497 if ( unlikely( !p_temp_buf
) )
499 memcpy( p_temp_buf
,p_pic_out
->p
[i_p
].p_pixels
,
500 p_pic_out
->p
[i_p
].i_lines
* p_pic_out
->p
[i_p
].i_pitch
);
502 /* copy lines to output_pic */
503 assert(p_pic_out
->p
[i_p
].i_visible_lines
> 0);
504 size_t i_visible_lines
= p_pic_out
->p
[i_p
].i_visible_lines
;
505 for ( size_t i_y
= 0; i_y
< i_visible_lines
; i_y
++ ) {
506 size_t i_ofs
= MOD( ( p_sys
->i_offset_ofs
+ p_sys
->i_sliding_ofs
)
508 p_sys
->i_height
[Y_PLANE
] );
509 i_ofs
*= i_visible_lines
;
510 i_ofs
/= p_sys
->i_height
[Y_PLANE
];
512 memcpy( &p_pic_out
->p
[i_p
].p_pixels
[ i_y
* p_pic_out
->p
[i_p
].i_pitch
],
513 &p_temp_buf
[ ( ( i_y
+ i_ofs
) % i_visible_lines
) * p_pic_out
->p
[i_p
].i_pitch
],
514 p_pic_out
->p
[i_p
].i_visible_pitch
);
523 * Black and white transform including a touch of sepia effect
525 static void oldmovie_black_n_white_effect( picture_t
*p_pic_out
)
527 assert(p_pic_out
->p
[Y_PLANE
].i_visible_lines
> 0);
528 assert(p_pic_out
->p
[Y_PLANE
].i_visible_pitch
> 0);
529 size_t i_visible_lines
= p_pic_out
->p
[Y_PLANE
].i_visible_lines
;
530 size_t i_visible_pitch
= p_pic_out
->p
[Y_PLANE
].i_visible_pitch
;
531 size_t i_pixel_pitch
= p_pic_out
->p
[Y_PLANE
].i_pixel_pitch
;
533 for ( size_t i_y
= 0; i_y
< i_visible_lines
; i_y
++ )
534 for ( size_t i_x
= 0; i_x
< i_visible_pitch
; i_x
+= i_pixel_pitch
) {
535 size_t i_pix_ofs
= i_x
+ i_y
* p_pic_out
->p
[Y_PLANE
].i_pitch
;
536 p_pic_out
->p
[Y_PLANE
].p_pixels
[i_pix_ofs
] -= p_pic_out
->p
[Y_PLANE
].p_pixels
[i_pix_ofs
] >> 2;
537 p_pic_out
->p
[Y_PLANE
].p_pixels
[i_pix_ofs
] += 30;
540 memset( p_pic_out
->p
[U_PLANE
].p_pixels
, 122,
541 p_pic_out
->p
[U_PLANE
].i_lines
* p_pic_out
->p
[U_PLANE
].i_pitch
);
542 memset( p_pic_out
->p
[V_PLANE
].p_pixels
, 132,
543 p_pic_out
->p
[V_PLANE
].i_lines
* p_pic_out
->p
[V_PLANE
].i_pitch
);
547 * Smooth darker borders effect
549 static int oldmovie_dark_border_effect( filter_t
*p_filter
, picture_t
*p_pic_out
)
551 filter_sys_t
*p_sys
= p_filter
->p_sys
;
553 #define BORDER_DIST 20
555 for ( int32_t i_y
= 0; i_y
< p_sys
->i_height
[Y_PLANE
]; i_y
++ )
556 for ( int32_t i_x
= 0; i_x
< p_sys
->i_width
[Y_PLANE
]; i_x
++ ) {
558 int32_t i_x_border_dist
= __MIN( i_x
, p_sys
->i_width
[Y_PLANE
] - i_x
);
559 int32_t i_y_border_dist
= __MIN( i_y
, p_sys
->i_height
[Y_PLANE
] - i_y
);
561 int32_t i_border_dist
= __MAX(BORDER_DIST
- i_x_border_dist
,0)
562 + __MAX(BORDER_DIST
- i_y_border_dist
,0);
564 i_border_dist
= __MIN(BORDER_DIST
, i_border_dist
);
566 if ( i_border_dist
== 0 )
569 uint32_t i_pix_ofs
= i_x
* p_pic_out
->p
[Y_PLANE
].i_pixel_pitch
570 + i_y
* p_pic_out
->p
[Y_PLANE
].i_pitch
;
572 SUB_MIN( p_pic_out
->p
[Y_PLANE
].p_pixels
[i_pix_ofs
],
573 i_border_dist
* 255 / BORDER_DIST
, 0 );
580 * Vertical scratch random management and effect
582 static int oldmovie_film_scratch_effect( filter_t
*p_filter
, picture_t
*p_pic_out
)
584 filter_sys_t
*p_sys
= p_filter
->p_sys
;
586 #define SCRATCH_GENERATOR_PERIOD VLC_TICK_FROM_SEC(2)
587 #define SCRATCH_DURATION VLC_TICK_FROM_MS(500)
589 /* generate new scratch */
590 if ( p_sys
->i_scratch_trigger
<= p_sys
->i_cur_time
) {
591 for ( uint32_t i_s
= 0; i_s
< MAX_SCRATCH
; i_s
++ )
592 if ( p_sys
->p_scratch
[i_s
] == NULL
) {
594 p_sys
->p_scratch
[i_s
] = calloc( 1, sizeof(scratch_t
) );
595 if ( unlikely( !p_sys
->p_scratch
[i_s
] ) )
598 /* set random parameters */
599 p_sys
->p_scratch
[i_s
]->i_offset
= ( ( (unsigned)vlc_mrand48() )
600 % __MAX( p_sys
->i_width
[Y_PLANE
] - 10, 1 ) )
602 p_sys
->p_scratch
[i_s
]->i_width
= ( ( (unsigned)vlc_mrand48() )
603 % __MAX( p_sys
->i_width
[Y_PLANE
] / 500, 1 ) )
605 p_sys
->p_scratch
[i_s
]->i_intensity
= (unsigned) vlc_mrand48() % 50 + 10;
606 p_sys
->p_scratch
[i_s
]->i_stop_trigger
= RandomEnd(p_sys
, SCRATCH_DURATION
);
610 p_sys
->i_scratch_trigger
= RandomEnd(p_sys
, SCRATCH_GENERATOR_PERIOD
);
613 /* manage and apply current scratch */
614 for ( uint32_t i_s
= 0; i_s
< MAX_SCRATCH
; i_s
++ )
615 if ( p_sys
->p_scratch
[i_s
] ) {
616 /* remove outdated scratch */
617 if ( p_sys
->p_scratch
[i_s
]->i_stop_trigger
<= p_sys
->i_cur_time
) {
618 FREENULL( p_sys
->p_scratch
[i_s
] );
622 /* otherwise apply scratch */
623 for ( int32_t i_y
= 0; i_y
< p_pic_out
->p
[Y_PLANE
].i_visible_lines
; i_y
++ )
624 for ( int32_t i_x
= p_sys
->p_scratch
[i_s
]->i_offset
;
625 i_x
< __MIN(p_sys
->p_scratch
[i_s
]->i_offset
626 + p_sys
->p_scratch
[i_s
]->i_width
, p_sys
->i_width
[Y_PLANE
] );
628 DARKEN_PIXEL( i_x
, i_y
, p_sys
->p_scratch
[i_s
]->i_intensity
,
629 &p_pic_out
->p
[Y_PLANE
] );
637 * bigger than dust but only during one frame (due to a local film damage)
639 static void oldmovie_film_blotch_effect( filter_t
*p_filter
, picture_t
*p_pic_out
)
641 filter_sys_t
*p_sys
= p_filter
->p_sys
;
643 #define BLOTCH_GENERATOR_PERIOD VLC_TICK_FROM_SEC(5)
645 /* generate blotch */
646 if ( p_sys
->i_blotch_trigger
<= p_sys
->i_cur_time
) {
647 /* set random parameters */
648 int32_t i_bx
= (unsigned)vlc_mrand48() % p_sys
->i_width
[Y_PLANE
];
649 int32_t i_by
= (unsigned)vlc_mrand48() % p_sys
->i_height
[Y_PLANE
];
650 int32_t i_width
= (unsigned)vlc_mrand48() % __MAX( 1, p_sys
->i_width
[Y_PLANE
] / 10 ) + 1;
651 int32_t i_intensity
= (unsigned)vlc_mrand48() % 50 + 20;
653 if ( (unsigned)vlc_mrand48() & 0x01 ) {
654 /* draw dark blotch */
655 for ( int32_t i_y
= -i_width
+ 1; i_y
< i_width
; i_y
++ )
656 for ( int32_t i_x
= -i_width
+ 1; i_x
< i_width
; i_x
++ )
657 if ( i_x
* i_x
+ i_y
* i_y
<= i_width
* i_width
)
658 CHECK_N_DARKEN_PIXEL( i_x
+ i_bx
, i_y
+ i_by
,
659 i_intensity
, &p_pic_out
->p
[Y_PLANE
] );
661 /* draw light blotch */
662 for ( int32_t i_y
= -i_width
+1; i_y
< i_width
; i_y
++ )
663 for ( int32_t i_x
= -i_width
+1; i_x
< i_width
; i_x
++ )
664 if ( i_x
* i_x
+ i_y
* i_y
<= i_width
* i_width
)
665 CHECK_N_LIGHTEN_PIXEL( i_x
+ i_bx
, i_y
+ i_by
,
666 i_intensity
, &p_pic_out
->p
[Y_PLANE
] );
669 p_sys
->i_blotch_trigger
= RandomEnd(p_sys
, BLOTCH_GENERATOR_PERIOD
);
674 * Dust dots addition, visible during one frame only (film damages)
676 static void oldmovie_film_dust_effect( filter_t
*p_filter
, picture_t
*p_pic_out
) {
677 #define ONESHOT_DUST_RATIO 1000
679 filter_sys_t
*p_sys
= p_filter
->p_sys
;
681 for ( int32_t i_dust
= 0;
682 i_dust
< p_sys
->i_width
[Y_PLANE
] * p_sys
->i_height
[Y_PLANE
] / ONESHOT_DUST_RATIO
;
684 if ( (unsigned)vlc_mrand48() % 5 < 3 )
685 DARKEN_PIXEL( (unsigned)vlc_mrand48() % p_sys
->i_width
[Y_PLANE
],
686 (unsigned)vlc_mrand48() % p_sys
->i_height
[Y_PLANE
],
687 150, &p_pic_out
->p
[Y_PLANE
] );
689 LIGHTEN_PIXEL( (unsigned)vlc_mrand48() % p_sys
->i_width
[Y_PLANE
],
690 (unsigned)vlc_mrand48() % p_sys
->i_height
[Y_PLANE
],
691 50, &p_pic_out
->p
[Y_PLANE
] );
695 * Hair and dust on projector lens
698 #define HAIR_GENERATOR_PERIOD VLC_TICK_FROM_SEC(50)
699 #define HAIR_DURATION VLC_TICK_FROM_SEC(50)
700 #define DUST_GENERATOR_PERIOD VLC_TICK_FROM_SEC(100)
701 #define DUST_DURATION VLC_TICK_FROM_SEC(4)
704 * Define hair location on the lens and timeout
707 static void oldmovie_define_hair_location( filter_t
*p_filter
, hair_t
* ps_hair
) {
708 filter_sys_t
*p_sys
= p_filter
->p_sys
;
710 ps_hair
->i_x
= (unsigned)vlc_mrand48() % p_sys
->i_width
[Y_PLANE
];
711 ps_hair
->i_y
= (unsigned)vlc_mrand48() % p_sys
->i_height
[Y_PLANE
];
712 ps_hair
->i_rotation
= (unsigned)vlc_mrand48() % 200;
714 ps_hair
->i_stop_trigger
= RandomEnd(p_sys
, HAIR_DURATION
);
718 * Show black hair on the screen
719 * after random duration it is removed or re-located
721 static int oldmovie_lens_hair_effect( filter_t
*p_filter
, picture_t
*p_pic_out
) {
722 filter_sys_t
*p_sys
= p_filter
->p_sys
;
724 /* generate new hair */
725 if ( p_sys
->i_hair_trigger
<= p_sys
->i_cur_time
) {
726 for ( uint32_t i_h
= 0; i_h
< MAX_HAIR
; i_h
++ )
727 if ( p_sys
->p_hair
[i_h
] == NULL
) {
729 p_sys
->p_hair
[i_h
] = calloc( 1, sizeof(hair_t
) );
730 if ( unlikely( !p_sys
->p_hair
[i_h
] ) )
733 /* set random parameters */
734 p_sys
->p_hair
[i_h
]->i_length
= (unsigned)vlc_mrand48()
735 % ( p_sys
->i_width
[Y_PLANE
] / 3 ) + 5;
736 p_sys
->p_hair
[i_h
]->i_curve
= MOD( (int32_t)vlc_mrand48(), 80 ) - 40;
737 p_sys
->p_hair
[i_h
]->i_width
= (unsigned)vlc_mrand48()
738 % __MAX( 1, p_sys
->i_width
[Y_PLANE
] / 1500 ) + 1;
739 p_sys
->p_hair
[i_h
]->i_intensity
= (unsigned)vlc_mrand48() % 50 + 20;
741 oldmovie_define_hair_location( p_filter
, p_sys
->p_hair
[i_h
] );
745 p_sys
->i_hair_trigger
= RandomEnd(p_sys
, HAIR_GENERATOR_PERIOD
);
748 /* manage and apply current hair */
749 for ( uint32_t i_h
= 0; i_h
< MAX_HAIR
; i_h
++ )
750 if ( p_sys
->p_hair
[i_h
] ) {
751 /* remove outdated ones */
752 if ( p_sys
->p_hair
[i_h
]->i_stop_trigger
<= p_sys
->i_cur_time
) {
753 /* select between moving or removing hair */
754 if ( (unsigned)vlc_mrand48() % 2 == 0 )
756 oldmovie_define_hair_location( p_filter
, p_sys
->p_hair
[i_h
] );
759 FREENULL( p_sys
->p_hair
[i_h
] );
765 double f_base_x
= (double)p_sys
->p_hair
[i_h
]->i_x
;
766 double f_base_y
= (double)p_sys
->p_hair
[i_h
]->i_y
;
768 for ( int32_t i_l
= 0; i_l
< p_sys
->p_hair
[i_h
]->i_length
; i_l
++ ) {
769 uint32_t i_current_rot
= p_sys
->p_hair
[i_h
]->i_rotation
770 + p_sys
->p_hair
[i_h
]->i_curve
* i_l
/ 100;
771 f_base_x
+= cos( (double)i_current_rot
/ 128.0 * M_PI
);
772 f_base_y
+= sin( (double)i_current_rot
/ 128.0 * M_PI
);
773 double f_current_x
= f_base_x
;
774 double f_current_y
= f_base_y
;
775 for ( int32_t i_w
= 0; i_w
< p_sys
->p_hair
[i_h
]->i_width
; i_w
++ ) {
776 f_current_x
+= sin( (double)i_current_rot
/ 128.0 * M_PI
);
777 f_current_y
+= cos( (double)i_current_rot
/ 128.0 * M_PI
);
778 CHECK_N_DARKEN_PIXEL( (int32_t) f_current_x
,
779 (int32_t) f_current_y
,
780 p_sys
->p_hair
[i_h
]->i_intensity
,
781 &p_pic_out
->p
[Y_PLANE
] );
790 * Define dust location on the lens and timeout
793 static void oldmovie_define_dust_location( filter_t
*p_filter
, dust_t
* ps_dust
) {
794 filter_sys_t
*p_sys
= p_filter
->p_sys
;
796 ps_dust
->i_x
= (unsigned)vlc_mrand48() % p_sys
->i_width
[Y_PLANE
];
797 ps_dust
->i_y
= (unsigned)vlc_mrand48() % p_sys
->i_height
[Y_PLANE
];
799 ps_dust
->i_stop_trigger
= RandomEnd(p_sys
, HAIR_DURATION
);
802 ps_dust
->i_x
= MOD( (int32_t)vlc_mrand48(), p_sys
->i_width
[Y_PLANE
] );
803 ps_dust
->i_y
= MOD( (int32_t)vlc_mrand48(), p_sys
->i_height
[Y_PLANE
] );
805 ps_dust
->i_stop_trigger
= RandomEnd(p_sys
, DUST_DURATION
);
810 * smaller than blotch but will remain on the screen for long time
812 static int oldmovie_lens_dust_effect( filter_t
*p_filter
, picture_t
*p_pic_out
) {
813 filter_sys_t
*p_sys
= p_filter
->p_sys
;
815 /* generate new dust */
816 if ( p_sys
->i_dust_trigger
<= p_sys
->i_cur_time
) {
817 for ( uint32_t i_d
= 0; i_d
< MAX_DUST
; i_d
++ )
818 if ( p_sys
->p_dust
[i_d
] == NULL
) {
820 p_sys
->p_dust
[i_d
] = calloc( 1, sizeof(dust_t
) );
821 if ( unlikely( !p_sys
->p_dust
[i_d
] ) )
824 /* set random parameters */
825 oldmovie_define_dust_location( p_filter
, p_sys
->p_dust
[i_d
] );
826 p_sys
->p_dust
[i_d
]->i_width
= MOD( (int32_t)vlc_mrand48(), 5 ) + 1;
827 p_sys
->p_dust
[i_d
]->i_intensity
= (unsigned)vlc_mrand48() % 30 + 30;
831 p_sys
->i_dust_trigger
= RandomEnd(p_sys
, DUST_GENERATOR_PERIOD
);
834 /* manage and apply current dust */
835 for ( uint32_t i_d
= 0; i_d
< MAX_DUST
; i_d
++ )
836 if ( p_sys
->p_dust
[i_d
] ) {
837 /* remove outdated ones */
838 if ( p_sys
->p_dust
[i_d
]->i_stop_trigger
<= p_sys
->i_cur_time
) {
839 /* select between moving or removing dust */
840 if ( (unsigned)vlc_mrand48() % 2 == 0 )
842 oldmovie_define_dust_location( p_filter
, p_sys
->p_dust
[i_d
] );
845 FREENULL( p_sys
->p_dust
[i_d
] );
851 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
++ )
852 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
++ )
853 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
)
854 CHECK_N_DARKEN_PIXEL( i_x
+ p_sys
->p_dust
[i_d
]->i_x
,
855 i_y
+ p_sys
->p_dust
[i_d
]->i_y
,
856 p_sys
->p_dust
[i_d
]->i_intensity
,
857 &p_pic_out
->p
[Y_PLANE
] );