mirror of https://code.videolan.org/videolan/vlc
Oldmovie: Oldmovie style video filter
Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
This commit is contained in:
parent
84fd16d63c
commit
4322d4210e
3
NEWS
3
NEWS
|
@ -8,6 +8,9 @@ Access:
|
|||
Video Output:
|
||||
* Direct rendering and filtering for VDPAU hardware acceleration
|
||||
|
||||
Video Filter:
|
||||
* New Oldmovie effect filter
|
||||
|
||||
Removed modules:
|
||||
* ios video output: use ios2
|
||||
|
||||
|
|
|
@ -231,6 +231,7 @@ $Id$
|
|||
* ntservice: run VLC as a NT service
|
||||
* nuv: NUV demuxer
|
||||
* ogg: input module for OGG decapsulation
|
||||
* oldmovie: oldmovie style video filter
|
||||
* oldrc: old interface module using stdio
|
||||
* omxil: OpenMAX IL audio/video decoder
|
||||
* omxil_vout: OpenMAX IL video output
|
||||
|
|
|
@ -133,6 +133,7 @@ SOURCES_gradfun = gradfun.c gradfun.h
|
|||
SOURCES_subsdelay = subsdelay.c
|
||||
SOURCES_hqdn3d = hqdn3d.c hqdn3d.h
|
||||
SOURCES_anaglyph = anaglyph.c
|
||||
SOURCES_oldmovie = oldmovie.c
|
||||
noinst_HEADERS = filter_picture.h
|
||||
|
||||
libvlc_LTLIBRARIES += \
|
||||
|
@ -175,4 +176,6 @@ libvlc_LTLIBRARIES += \
|
|||
libyuvp_plugin.la \
|
||||
libantiflicker_plugin.la \
|
||||
libhqdn3d_plugin.la \
|
||||
libanaglyph_plugin.la
|
||||
libanaglyph_plugin.la \
|
||||
liboldmovie_plugin.la
|
||||
|
||||
|
|
|
@ -0,0 +1,874 @@
|
|||
/*****************************************************************************
|
||||
* oldmovie.c : Old movie effect video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2013 Vianney Boyer
|
||||
* $Id$
|
||||
*
|
||||
* Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Preamble
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_filter.h>
|
||||
#include <vlc_rand.h>
|
||||
#include <vlc_mtime.h>
|
||||
|
||||
#include "filter_picture.h"
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef TIME_UNIT_PER_S
|
||||
# define TIME_UNIT_PER_S ( ((int64_t) 1) << 32 )
|
||||
#endif
|
||||
|
||||
static inline int64_t MOD(int64_t a, int64_t b) {
|
||||
return ( ( a % b ) + b ) % b; }
|
||||
|
||||
#define SUB_MIN(val, sub_val, min) val = \
|
||||
((val-(int32_t)sub_val)<min?min:val-sub_val)
|
||||
#define ADD_MAX(val, add_val, max) val = \
|
||||
((val+(int32_t)add_val)>max?max:val+add_val)
|
||||
|
||||
static inline int32_t PIX_OFS(int32_t i_x, int32_t i_y, plane_t *ps_plane) {
|
||||
return i_x * ps_plane->i_pixel_pitch + i_y * ps_plane->i_pitch; }
|
||||
|
||||
#define CHECK_PIX_OFS(i_x, i_y, ps_plane) ( \
|
||||
(i_x) >= 0 && (i_y) >= 0 && \
|
||||
(i_x) * ps_plane->i_pixel_pitch < ps_plane->i_visible_pitch && \
|
||||
(i_y) < ps_plane->i_visible_lines \
|
||||
)
|
||||
|
||||
static inline void DARKEN_PIXEL(int32_t i_x, int32_t i_y,
|
||||
int16_t intensity, plane_t *ps_plane) {
|
||||
SUB_MIN( ps_plane->p_pixels[ PIX_OFS(i_x, i_y, ps_plane) ],
|
||||
intensity, 0 );
|
||||
}
|
||||
|
||||
static inline void LIGHTEN_PIXEL(int32_t i_x, int32_t i_y,
|
||||
int16_t intensity, plane_t *ps_plane) {
|
||||
ADD_MAX( ps_plane->p_pixels[ PIX_OFS(i_x, i_y, ps_plane) ],
|
||||
intensity, 0xFF );
|
||||
}
|
||||
|
||||
static inline void CHECK_N_DARKEN_PIXEL(int32_t i_x, int32_t i_y,
|
||||
int16_t intensity, plane_t *ps_plane) {
|
||||
if ( likely( CHECK_PIX_OFS(i_x, i_y, ps_plane) ) )
|
||||
DARKEN_PIXEL(i_x, i_y, intensity, ps_plane);
|
||||
}
|
||||
|
||||
static inline void CHECK_N_LIGHTEN_PIXEL(int32_t i_x, int32_t i_y,
|
||||
int16_t intensity, plane_t *ps_plane) {
|
||||
if ( likely( CHECK_PIX_OFS(i_x, i_y, ps_plane) ) )
|
||||
LIGHTEN_PIXEL(i_x, i_y, intensity, ps_plane);
|
||||
}
|
||||
|
||||
#define MAX_SCRATCH 20
|
||||
#define MAX_HAIR 10
|
||||
#define MAX_DUST 10
|
||||
|
||||
typedef struct {
|
||||
int32_t i_offset;
|
||||
int32_t i_width;
|
||||
uint16_t i_intensity;
|
||||
uint64_t i_stop_trigger;
|
||||
} scratch_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t i_x, i_y;
|
||||
uint8_t i_rotation;
|
||||
int32_t i_width;
|
||||
int32_t i_length;
|
||||
int32_t i_curve;
|
||||
uint16_t i_intensity;
|
||||
uint64_t i_stop_trigger;
|
||||
} hair_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t i_x, i_y;
|
||||
int32_t i_width;
|
||||
uint16_t i_intensity;
|
||||
uint64_t i_stop_trigger;
|
||||
} dust_t;
|
||||
|
||||
struct filter_sys_t {
|
||||
|
||||
/* general data */
|
||||
bool b_init;
|
||||
int32_t i_planes;
|
||||
int32_t *i_height;
|
||||
int32_t *i_width;
|
||||
int32_t *i_visible_pitch;
|
||||
uint64_t i_start_time;
|
||||
uint64_t i_last_time;
|
||||
uint64_t i_cur_time;
|
||||
|
||||
/* sliding & offset effect */
|
||||
uint64_t i_offset_trigger;
|
||||
uint64_t i_sliding_trigger;
|
||||
uint64_t i_sliding_stop_trig;
|
||||
int32_t i_offset_ofs;
|
||||
int32_t i_sliding_ofs;
|
||||
int32_t i_sliding_speed;
|
||||
|
||||
/* scratch on film */
|
||||
uint64_t i_scratch_trigger;
|
||||
scratch_t *p_scratch[MAX_SCRATCH];
|
||||
|
||||
/* hair on lens */
|
||||
uint64_t i_hair_trigger;
|
||||
hair_t *p_hair[MAX_HAIR];
|
||||
|
||||
/* blotch on film */
|
||||
uint64_t i_blotch_trigger;
|
||||
|
||||
/* dust on lens */
|
||||
uint64_t i_dust_trigger;
|
||||
dust_t *p_dust[MAX_DUST];
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* Prototypes
|
||||
*****************************************************************************/
|
||||
|
||||
picture_t *Filter( filter_t *, picture_t * );
|
||||
|
||||
int oldmovie_allocate_data( filter_t *, picture_t * );
|
||||
void oldmovie_free_allocated_data( filter_t * );
|
||||
|
||||
void oldmovie_shutter_effect( filter_t *, picture_t * );
|
||||
int oldmovie_sliding_offset_effect( filter_t *, picture_t * );
|
||||
void oldmovie_black_n_white_effect( picture_t * );
|
||||
int oldmovie_dark_border_effect( filter_t *, picture_t * );
|
||||
int oldmovie_film_scratch_effect( filter_t *, picture_t * );
|
||||
void oldmovie_film_blotch_effect( filter_t *, picture_t * );
|
||||
void oldmovie_film_dust_effect( filter_t *, picture_t * );
|
||||
int oldmovie_lens_hair_effect( filter_t *, picture_t * );
|
||||
int oldmovie_lens_dust_effect( filter_t *, picture_t * );
|
||||
|
||||
void oldmovie_define_hair_location( filter_t *p_filter, hair_t* ps_hair );
|
||||
void oldmovie_define_dust_location( filter_t *p_filter, dust_t* ps_dust );
|
||||
int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out );
|
||||
|
||||
/*****************************************************************************
|
||||
* Module descriptor
|
||||
*****************************************************************************/
|
||||
|
||||
int Open ( vlc_object_t * );
|
||||
void Close( vlc_object_t * );
|
||||
|
||||
vlc_module_begin()
|
||||
set_description( N_("Old movie effect video filter") )
|
||||
set_shortname( N_( "Old movie" ))
|
||||
set_capability( "video filter2", 0 )
|
||||
set_category( CAT_VIDEO )
|
||||
set_subcategory( SUBCAT_VIDEO_VFILTER )
|
||||
|
||||
set_callbacks( Open, Close )
|
||||
vlc_module_end()
|
||||
|
||||
/**
|
||||
* Open the filter
|
||||
*/
|
||||
int Open( vlc_object_t *p_this ) {
|
||||
filter_t *p_filter = (filter_t *)p_this;
|
||||
filter_sys_t *p_sys;
|
||||
|
||||
/* Assert video in match with video out */
|
||||
if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
|
||||
msg_Err( p_filter, "Input and output format does not match" );
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
/* Reject 0 bpp and unsupported chroma */
|
||||
const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
|
||||
const vlc_chroma_description_t *p_chroma =
|
||||
vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
|
||||
if( !p_chroma || p_chroma->pixel_size == 0
|
||||
|| p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
|
||||
|| !vlc_fourcc_IsYUV( fourcc ) ) {
|
||||
|
||||
msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
/* Allocate structure */
|
||||
p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
|
||||
if( unlikely( !p_sys ) )
|
||||
return VLC_ENOMEM;
|
||||
|
||||
/* init data */
|
||||
p_filter->pf_video_filter = Filter;
|
||||
p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = NTPtime64();
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the filter
|
||||
*/
|
||||
void Close( vlc_object_t *p_this ) {
|
||||
filter_t *p_filter = (filter_t *)p_this;
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
/* Free allocated memory */
|
||||
oldmovie_free_allocated_data( p_filter );
|
||||
free( p_sys );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a picture
|
||||
*/
|
||||
picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
|
||||
if( unlikely( !p_pic_in || !p_filter ) )
|
||||
return NULL;
|
||||
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
picture_t *p_pic_out = filter_NewPicture( p_filter );
|
||||
if( unlikely( !p_pic_out ) ) {
|
||||
picture_Release( p_pic_in );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* manage time
|
||||
*/
|
||||
p_sys->i_last_time = p_sys->i_cur_time;
|
||||
p_sys->i_cur_time = NTPtime64();
|
||||
|
||||
/*
|
||||
* allocate data
|
||||
*/
|
||||
if ( unlikely( !p_sys->b_init ) )
|
||||
if ( unlikely( oldmovie_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
|
||||
picture_Release( p_pic_in );
|
||||
return NULL;
|
||||
}
|
||||
p_sys->b_init = true;
|
||||
|
||||
/*
|
||||
* preset output pic: raw copy src to dst
|
||||
*/
|
||||
picture_CopyPixels(p_pic_out, p_pic_in);
|
||||
|
||||
/*
|
||||
* apply several effects on picture
|
||||
*/
|
||||
oldmovie_black_n_white_effect( p_pic_out );
|
||||
|
||||
/* simulates projector shutter blinking effect */
|
||||
oldmovie_shutter_effect(p_filter, p_pic_out);
|
||||
|
||||
if ( unlikely( oldmovie_sliding_offset_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
|
||||
return CopyInfoAndRelease( p_pic_out, p_pic_in );
|
||||
|
||||
oldmovie_dark_border_effect( p_filter, p_pic_out );
|
||||
|
||||
if ( unlikely( oldmovie_film_scratch_effect(p_filter, p_pic_out) != VLC_SUCCESS ) )
|
||||
return CopyInfoAndRelease( p_pic_out, p_pic_in );
|
||||
|
||||
oldmovie_film_blotch_effect(p_filter, p_pic_out);
|
||||
|
||||
if ( unlikely( oldmovie_lens_hair_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
|
||||
return CopyInfoAndRelease( p_pic_out, p_pic_in );
|
||||
|
||||
if ( unlikely( oldmovie_lens_dust_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
|
||||
return CopyInfoAndRelease( p_pic_out, p_pic_in );
|
||||
|
||||
oldmovie_film_dust_effect( p_filter, p_pic_out );
|
||||
|
||||
return CopyInfoAndRelease( p_pic_out, p_pic_in );
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate data
|
||||
*/
|
||||
int oldmovie_allocate_data( filter_t *p_filter, picture_t *p_pic_in ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
oldmovie_free_allocated_data( p_filter );
|
||||
|
||||
/*
|
||||
* take into account different characteristics for each plane
|
||||
*/
|
||||
p_sys->i_planes = p_pic_in->i_planes;
|
||||
p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
|
||||
p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
|
||||
p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
|
||||
|
||||
if( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) ) {
|
||||
oldmovie_free_allocated_data( p_filter );
|
||||
return VLC_ENOMEM;
|
||||
}
|
||||
|
||||
for (int32_t i_p=0; i_p < p_sys->i_planes; i_p++) {
|
||||
p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
|
||||
p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
|
||||
p_sys->i_width [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch
|
||||
/ p_pic_in->p[i_p].i_pixel_pitch;
|
||||
}
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free allocated data
|
||||
*/
|
||||
void oldmovie_free_allocated_data( filter_t *p_filter ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
|
||||
FREENULL(p_sys->p_scratch[i_s]);
|
||||
|
||||
for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
|
||||
FREENULL(p_sys->p_hair[i_h]);
|
||||
|
||||
for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
|
||||
FREENULL(p_sys->p_dust[i_d]);
|
||||
|
||||
p_sys->i_planes = 0;
|
||||
FREENULL( p_sys->i_height );
|
||||
FREENULL( p_sys->i_width );
|
||||
FREENULL( p_sys->i_visible_pitch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Projector shutter effect
|
||||
*/
|
||||
void oldmovie_shutter_effect( filter_t *p_filter, picture_t *p_pic_out ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
#define SHUTTER_FREQ 2
|
||||
#define SHUTTER_SPEED 25
|
||||
#define SHUTTER_HEIGHT 1.5
|
||||
|
||||
#define SHUTTER_INTENSITY 50
|
||||
|
||||
#define SUB_FRAME (p_sys->i_cur_time % (TIME_UNIT_PER_S / SHUTTER_FREQ))
|
||||
|
||||
/*
|
||||
* depending on current time: define shutter location on picture
|
||||
*/
|
||||
int32_t i_shutter_sup = VLC_CLIP((int64_t)SUB_FRAME
|
||||
* p_pic_out->p[Y_PLANE].i_visible_lines
|
||||
* SHUTTER_SPEED / TIME_UNIT_PER_S,
|
||||
0, p_pic_out->p[Y_PLANE].i_visible_lines);
|
||||
|
||||
int32_t i_shutter_inf = VLC_CLIP((int64_t)SUB_FRAME
|
||||
* p_pic_out->p[Y_PLANE].i_visible_lines
|
||||
* SHUTTER_SPEED / TIME_UNIT_PER_S
|
||||
- SHUTTER_HEIGHT * p_pic_out->p[Y_PLANE].i_visible_lines,
|
||||
0, p_pic_out->p[Y_PLANE].i_visible_lines);
|
||||
|
||||
int32_t i_width = p_pic_out->p[Y_PLANE].i_visible_pitch
|
||||
/ p_pic_out->p[Y_PLANE].i_pixel_pitch;
|
||||
|
||||
/*
|
||||
* darken pixels currently occulted by shutter
|
||||
*/
|
||||
for ( int32_t i_y = i_shutter_inf; i_y < i_shutter_sup; i_y++ )
|
||||
for ( int32_t i_x = 0; i_x < i_width; i_x++ )
|
||||
DARKEN_PIXEL( i_x, i_y, SHUTTER_INTENSITY, &p_pic_out->p[Y_PLANE] );
|
||||
}
|
||||
|
||||
/**
|
||||
* sliding & offset effect
|
||||
*/
|
||||
int oldmovie_sliding_offset_effect( filter_t *p_filter, picture_t *p_pic_out ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
|
||||
/**
|
||||
* one shot offset section
|
||||
*/
|
||||
|
||||
#define OFFSET_AVERAGE_PERIOD (10 * TIME_UNIT_PER_S)
|
||||
|
||||
/* start trigger to be (re)initialized */
|
||||
if ( p_sys->i_offset_trigger == 0
|
||||
|| p_sys->i_sliding_speed != 0 ) { /* do not mix sliding and offset */
|
||||
/* random trigger for offset effect */
|
||||
p_sys->i_offset_trigger = p_sys->i_cur_time
|
||||
+ ( (uint64_t) vlc_mrand48() ) % OFFSET_AVERAGE_PERIOD
|
||||
+ OFFSET_AVERAGE_PERIOD / 2;
|
||||
p_sys->i_offset_ofs = 0;
|
||||
} else if ( p_sys->i_offset_trigger <= p_sys->i_cur_time ) {
|
||||
/* trigger for offset effect */
|
||||
p_sys->i_offset_trigger = 0;
|
||||
p_sys->i_offset_ofs = MOD( ( (uint32_t)vlc_mrand48() ),
|
||||
p_sys->i_height[Y_PLANE] ) * 100;
|
||||
} else
|
||||
p_sys->i_offset_ofs = 0;
|
||||
|
||||
|
||||
/**
|
||||
* sliding section
|
||||
*/
|
||||
|
||||
#define SLIDING_AVERAGE_PERIOD (20 * TIME_UNIT_PER_S)
|
||||
#define SLIDING_AVERAGE_DURATION ( 3 * TIME_UNIT_PER_S)
|
||||
|
||||
/* start trigger to be (re)initialized */
|
||||
if ( ( p_sys->i_sliding_stop_trig == 0 )
|
||||
&& ( p_sys->i_sliding_trigger == 0 )
|
||||
&& ( p_sys->i_sliding_speed == 0 ) ) {
|
||||
/* random trigger which enable sliding effect */
|
||||
p_sys->i_sliding_trigger = p_sys->i_cur_time
|
||||
+ ( (uint64_t) vlc_mrand48() ) % SLIDING_AVERAGE_PERIOD
|
||||
+ SLIDING_AVERAGE_PERIOD / 2;
|
||||
}
|
||||
/* start trigger just occurs */
|
||||
else if ( ( p_sys->i_sliding_stop_trig == 0 )
|
||||
&& ( p_sys->i_sliding_trigger <= p_sys->i_cur_time )
|
||||
&& ( p_sys->i_sliding_speed == 0 ) ) {
|
||||
/* init sliding parameters */
|
||||
p_sys->i_sliding_trigger = 0;
|
||||
p_sys->i_sliding_stop_trig = p_sys->i_cur_time
|
||||
+ ((uint64_t) vlc_mrand48() ) % SLIDING_AVERAGE_DURATION
|
||||
+ SLIDING_AVERAGE_DURATION / 2;
|
||||
p_sys->i_sliding_ofs = 0;
|
||||
/* note: sliding speed unit = image per 100 s */
|
||||
p_sys->i_sliding_speed = MOD(((int32_t) vlc_mrand48() ), 201) - 100;
|
||||
}
|
||||
/* stop trigger disabling sliding effect */
|
||||
else if ( ( p_sys->i_sliding_stop_trig <= p_sys->i_cur_time )
|
||||
&& ( p_sys->i_sliding_trigger == 0 ) ) {
|
||||
|
||||
/* first increase speed to ensure we will come back to stable image */
|
||||
if ( abs(p_sys->i_sliding_speed) < 50 )
|
||||
p_sys->i_sliding_speed += 5;
|
||||
|
||||
/* check if offset is close to 0 and then ready to stop */
|
||||
if ( abs( p_sys->i_sliding_ofs ) < abs( p_sys->i_sliding_speed
|
||||
* p_sys->i_height[Y_PLANE]
|
||||
* ( p_sys->i_cur_time - p_sys->i_last_time ) / TIME_UNIT_PER_S )
|
||||
|| abs( p_sys->i_sliding_ofs ) < p_sys->i_height[Y_PLANE] * 100 / 20 ) {
|
||||
|
||||
/* reset sliding parameters */
|
||||
p_sys->i_sliding_ofs = p_sys->i_sliding_speed = 0;
|
||||
p_sys->i_sliding_trigger = p_sys->i_sliding_stop_trig = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* update offset */
|
||||
p_sys->i_sliding_ofs += p_sys->i_sliding_speed * p_sys->i_height[Y_PLANE]
|
||||
* ( p_sys->i_cur_time - p_sys->i_last_time )
|
||||
/ TIME_UNIT_PER_S;
|
||||
|
||||
p_sys->i_sliding_ofs = MOD( p_sys->i_sliding_ofs,
|
||||
p_sys->i_height[Y_PLANE] * 100 );
|
||||
|
||||
/* apply offset */
|
||||
return oldmovie_sliding_offset_apply( p_filter, p_pic_out );
|
||||
}
|
||||
|
||||
/**
|
||||
* apply both sliding and offset effect
|
||||
*/
|
||||
int oldmovie_sliding_offset_apply( filter_t *p_filter, picture_t *p_pic_out )
|
||||
{
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
for ( uint8_t i_p = 0; i_p < p_pic_out->i_planes; i_p++ ) {
|
||||
/* first allocate temporary buffer for swap operation */
|
||||
uint8_t *p_temp_buf = calloc( p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch,
|
||||
sizeof(uint8_t) );
|
||||
if ( unlikely( !p_temp_buf ) )
|
||||
return VLC_ENOMEM;
|
||||
memcpy( p_temp_buf,p_pic_out->p[i_p].p_pixels,
|
||||
p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch );
|
||||
|
||||
/* copy lines to output_pic */
|
||||
for ( int32_t i_y = 0; i_y < p_pic_out->p[i_p].i_visible_lines; i_y++ ) {
|
||||
int32_t i_ofs = MOD( ( p_sys->i_offset_ofs + p_sys->i_sliding_ofs )
|
||||
/100,
|
||||
p_sys->i_height[Y_PLANE] );
|
||||
i_ofs *= p_pic_out->p[i_p].i_visible_lines;
|
||||
i_ofs /= p_sys->i_height[Y_PLANE];
|
||||
|
||||
memcpy( &p_pic_out->p[i_p].p_pixels[ i_y * p_pic_out->p[i_p].i_pitch ],
|
||||
&p_temp_buf[ ( ( i_y + i_ofs ) % p_pic_out->p[i_p].i_visible_lines ) * p_pic_out->p[i_p].i_pitch ],
|
||||
p_pic_out->p[i_p].i_visible_pitch);
|
||||
}
|
||||
free( p_temp_buf );
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Black and white transform including a touch of sepia effect
|
||||
*/
|
||||
void oldmovie_black_n_white_effect( picture_t *p_pic_out )
|
||||
{
|
||||
for ( int32_t i_y = 0; i_y < p_pic_out->p[Y_PLANE].i_visible_lines; i_y++ )
|
||||
for ( int32_t i_x = 0; i_x < p_pic_out->p[Y_PLANE].i_visible_pitch;
|
||||
i_x += p_pic_out->p[Y_PLANE].i_pixel_pitch ) {
|
||||
|
||||
uint32_t i_pix_ofs = i_x+i_y*p_pic_out->p[Y_PLANE].i_pitch;
|
||||
p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] -= p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] >> 2;
|
||||
p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs] += 30;
|
||||
}
|
||||
|
||||
memset( p_pic_out->p[U_PLANE].p_pixels, 122,
|
||||
p_pic_out->p[U_PLANE].i_lines * p_pic_out->p[U_PLANE].i_pitch );
|
||||
memset( p_pic_out->p[V_PLANE].p_pixels, 132,
|
||||
p_pic_out->p[V_PLANE].i_lines * p_pic_out->p[V_PLANE].i_pitch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Smooth darker borders effect
|
||||
*/
|
||||
int oldmovie_dark_border_effect( filter_t *p_filter, picture_t *p_pic_out )
|
||||
{
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
#define BORDER_DIST 20
|
||||
|
||||
for ( int32_t i_y = 0; i_y < p_sys->i_height[Y_PLANE]; i_y++ )
|
||||
for ( int32_t i_x = 0; i_x < p_sys->i_width[Y_PLANE]; i_x++ ) {
|
||||
|
||||
int32_t i_x_border_dist = __MIN( i_x, p_sys->i_width[Y_PLANE] - i_x);
|
||||
int32_t i_y_border_dist = __MIN( i_y, p_sys->i_height[Y_PLANE] - i_y);
|
||||
|
||||
int32_t i_border_dist = __MAX(BORDER_DIST - i_x_border_dist,0)
|
||||
+ __MAX(BORDER_DIST - i_y_border_dist,0);
|
||||
|
||||
i_border_dist = __MIN(BORDER_DIST, i_border_dist);
|
||||
|
||||
if ( i_border_dist == 0 )
|
||||
continue;
|
||||
|
||||
uint32_t i_pix_ofs = i_x * p_pic_out->p[Y_PLANE].i_pixel_pitch
|
||||
+ i_y * p_pic_out->p[Y_PLANE].i_pitch;
|
||||
|
||||
SUB_MIN( p_pic_out->p[Y_PLANE].p_pixels[i_pix_ofs],
|
||||
i_border_dist * 255 / BORDER_DIST, 0 );
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical scratch random management and effect
|
||||
*/
|
||||
int oldmovie_film_scratch_effect( filter_t *p_filter, picture_t *p_pic_out )
|
||||
{
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
#define SCRATCH_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 2 )
|
||||
#define SCRATCH_DURATION ( TIME_UNIT_PER_S * 1 / 2)
|
||||
|
||||
/* generate new scratch */
|
||||
if ( p_sys->i_scratch_trigger <= p_sys->i_cur_time ) {
|
||||
for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
|
||||
if ( p_sys->p_scratch[i_s] == NULL ) {
|
||||
/* allocate data */
|
||||
p_sys->p_scratch[i_s] = calloc( 1, sizeof(scratch_t) );
|
||||
if ( unlikely( !p_sys->p_scratch[i_s] ) )
|
||||
return VLC_ENOMEM;
|
||||
|
||||
/* set random parameters */
|
||||
p_sys->p_scratch[i_s]->i_offset = ( ( (unsigned)vlc_mrand48() )
|
||||
% __MAX( p_sys->i_width[Y_PLANE] - 10, 1 ) )
|
||||
+ 5;
|
||||
p_sys->p_scratch[i_s]->i_width = ( ( (unsigned)vlc_mrand48() )
|
||||
% __MAX( p_sys->i_width[Y_PLANE] / 500, 1 ) )
|
||||
+ 1;
|
||||
p_sys->p_scratch[i_s]->i_intensity = (unsigned) vlc_mrand48() % 50 + 10;
|
||||
p_sys->p_scratch[i_s]->i_stop_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t) vlc_mrand48() % SCRATCH_DURATION
|
||||
+ SCRATCH_DURATION / 2;
|
||||
|
||||
break;
|
||||
}
|
||||
p_sys->i_scratch_trigger = p_sys->i_cur_time
|
||||
+ ( (uint64_t)vlc_mrand48() ) % SCRATCH_GENERATOR_PERIOD
|
||||
+ SCRATCH_GENERATOR_PERIOD / 2;
|
||||
}
|
||||
|
||||
/* manage and apply current scratch */
|
||||
for ( uint32_t i_s = 0; i_s < MAX_SCRATCH; i_s++ )
|
||||
if ( p_sys->p_scratch[i_s] ) {
|
||||
/* remove outdated scratch */
|
||||
if ( p_sys->p_scratch[i_s]->i_stop_trigger <= p_sys->i_cur_time ) {
|
||||
FREENULL( p_sys->p_scratch[i_s] );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* otherwise apply scratch */
|
||||
for ( int32_t i_y = 0; i_y < p_pic_out->p[Y_PLANE].i_visible_lines; i_y++ )
|
||||
for ( int32_t i_x = p_sys->p_scratch[i_s]->i_offset;
|
||||
i_x < __MIN(p_sys->p_scratch[i_s]->i_offset
|
||||
+ p_sys->p_scratch[i_s]->i_width, p_sys->i_width[Y_PLANE] );
|
||||
i_x++ )
|
||||
DARKEN_PIXEL( i_x, i_y, p_sys->p_scratch[i_s]->i_intensity,
|
||||
&p_pic_out->p[Y_PLANE] );
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blotch addition
|
||||
* bigger than dust but only during one frame (due to a local film damage)
|
||||
*/
|
||||
void oldmovie_film_blotch_effect( filter_t *p_filter, picture_t *p_pic_out )
|
||||
{
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
#define BLOTCH_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 5 )
|
||||
|
||||
/* generate blotch */
|
||||
if ( p_sys->i_blotch_trigger <= p_sys->i_cur_time ) {
|
||||
/* set random parameters */
|
||||
int32_t i_bx = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
|
||||
int32_t i_by = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
|
||||
int32_t i_width = (unsigned)vlc_mrand48() % __MAX( 1, p_sys->i_width[Y_PLANE] / 10 ) + 1;
|
||||
int32_t i_intensity = (unsigned)vlc_mrand48() % 50 + 20;
|
||||
|
||||
if ( (unsigned)vlc_mrand48() & 0x01 ) {
|
||||
/* draw dark blotch */
|
||||
for ( int32_t i_y = -i_width + 1; i_y < i_width; i_y++ )
|
||||
for ( int32_t i_x = -i_width + 1; i_x < i_width; i_x++ )
|
||||
if ( i_x * i_x + i_y * i_y <= i_width * i_width )
|
||||
CHECK_N_DARKEN_PIXEL( i_x + i_bx, i_y + i_by,
|
||||
i_intensity, &p_pic_out->p[Y_PLANE] );
|
||||
} else {
|
||||
/* draw light blotch */
|
||||
for ( int32_t i_y = -i_width+1; i_y < i_width; i_y++ )
|
||||
for ( int32_t i_x = -i_width+1; i_x < i_width; i_x++ )
|
||||
if ( i_x * i_x + i_y * i_y <= i_width * i_width )
|
||||
CHECK_N_LIGHTEN_PIXEL( i_x + i_bx, i_y + i_by,
|
||||
i_intensity, &p_pic_out->p[Y_PLANE] );
|
||||
}
|
||||
|
||||
p_sys->i_blotch_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t)vlc_mrand48() % BLOTCH_GENERATOR_PERIOD
|
||||
+ BLOTCH_GENERATOR_PERIOD / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dust dots addition, visible during one frame only (film damages)
|
||||
*/
|
||||
void oldmovie_film_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
|
||||
#define ONESHOT_DUST_RATIO 1000
|
||||
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
for ( int32_t i_dust = 0;
|
||||
i_dust < p_sys->i_width[Y_PLANE] * p_sys->i_height[Y_PLANE] / ONESHOT_DUST_RATIO;
|
||||
i_dust++)
|
||||
if ( (unsigned)vlc_mrand48() % 5 < 3 )
|
||||
DARKEN_PIXEL( (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE],
|
||||
(unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE],
|
||||
150, &p_pic_out->p[Y_PLANE] );
|
||||
else
|
||||
LIGHTEN_PIXEL( (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE],
|
||||
(unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE],
|
||||
50, &p_pic_out->p[Y_PLANE] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hair and dust on projector lens
|
||||
*
|
||||
*/
|
||||
#define HAIR_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 50 )
|
||||
#define HAIR_DURATION ( TIME_UNIT_PER_S * 50 )
|
||||
#define DUST_GENERATOR_PERIOD ( TIME_UNIT_PER_S * 100 )
|
||||
#define DUST_DURATION ( TIME_UNIT_PER_S * 4 )
|
||||
|
||||
/**
|
||||
* Define hair location on the lens and timeout
|
||||
*
|
||||
*/
|
||||
void oldmovie_define_hair_location( filter_t *p_filter, hair_t* ps_hair ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
ps_hair->i_x = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
|
||||
ps_hair->i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
|
||||
ps_hair->i_rotation = (unsigned)vlc_mrand48() % 200;
|
||||
|
||||
ps_hair->i_stop_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t)vlc_mrand48() % HAIR_DURATION
|
||||
+ HAIR_DURATION / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show black hair on the screen
|
||||
* after random duration it is removed or re-located
|
||||
*/
|
||||
int oldmovie_lens_hair_effect( filter_t *p_filter, picture_t *p_pic_out ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
/* generate new hair */
|
||||
if ( p_sys->i_hair_trigger <= p_sys->i_cur_time ) {
|
||||
for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
|
||||
if ( p_sys->p_hair[i_h] == NULL ) {
|
||||
/* allocate data */
|
||||
p_sys->p_hair[i_h] = calloc( 1, sizeof(hair_t) );
|
||||
if ( unlikely( !p_sys->p_hair[i_h] ) )
|
||||
return VLC_ENOMEM;
|
||||
|
||||
/* set random parameters */
|
||||
p_sys->p_hair[i_h]->i_length = (unsigned)vlc_mrand48()
|
||||
% ( p_sys->i_width[Y_PLANE] / 3 ) + 5;
|
||||
p_sys->p_hair[i_h]->i_curve = MOD( (int32_t)vlc_mrand48(), 80 ) - 40;
|
||||
p_sys->p_hair[i_h]->i_width = (unsigned)vlc_mrand48()
|
||||
% __MAX( 1, p_sys->i_width[Y_PLANE] / 1500 ) + 1;
|
||||
p_sys->p_hair[i_h]->i_intensity = (unsigned)vlc_mrand48() % 50 + 20;
|
||||
|
||||
oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
|
||||
|
||||
break;
|
||||
}
|
||||
p_sys->i_hair_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t)vlc_mrand48() % HAIR_GENERATOR_PERIOD
|
||||
+ HAIR_GENERATOR_PERIOD / 2;
|
||||
}
|
||||
|
||||
/* manage and apply current hair */
|
||||
for ( uint32_t i_h = 0; i_h < MAX_HAIR; i_h++ )
|
||||
if ( p_sys->p_hair[i_h] ) {
|
||||
/* remove outdated ones */
|
||||
if ( p_sys->p_hair[i_h]->i_stop_trigger <= p_sys->i_cur_time ) {
|
||||
/* select between moving or removing hair */
|
||||
if ( (unsigned)vlc_mrand48() % 2 == 0 )
|
||||
/* move hair */
|
||||
oldmovie_define_hair_location( p_filter, p_sys->p_hair[i_h] );
|
||||
else {
|
||||
/* remove hair */
|
||||
FREENULL( p_sys->p_hair[i_h] );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* draw hair */
|
||||
double f_base_x = (double)p_sys->p_hair[i_h]->i_x;
|
||||
double f_base_y = (double)p_sys->p_hair[i_h]->i_y;
|
||||
|
||||
for ( int32_t i_l = 0; i_l < p_sys->p_hair[i_h]->i_length; i_l++ ) {
|
||||
uint32_t i_current_rot = p_sys->p_hair[i_h]->i_rotation
|
||||
+ p_sys->p_hair[i_h]->i_curve * i_l / 100;
|
||||
f_base_x += cos( (double)i_current_rot / 128.0 * M_PI );
|
||||
f_base_y += sin( (double)i_current_rot / 128.0 * M_PI );
|
||||
double f_current_x = f_base_x;
|
||||
double f_current_y = f_base_y;
|
||||
for ( int32_t i_w = 0; i_w < p_sys->p_hair[i_h]->i_width; i_w++ ) {
|
||||
f_current_x += sin( (double)i_current_rot / 128.0 * M_PI );
|
||||
f_current_y += cos( (double)i_current_rot / 128.0 * M_PI );
|
||||
CHECK_N_DARKEN_PIXEL( (int32_t) f_current_x,
|
||||
(int32_t) f_current_y,
|
||||
p_sys->p_hair[i_h]->i_intensity,
|
||||
&p_pic_out->p[Y_PLANE] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define dust location on the lens and timeout
|
||||
*
|
||||
*/
|
||||
void oldmovie_define_dust_location( filter_t *p_filter, dust_t* ps_dust ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
ps_dust->i_x = (unsigned)vlc_mrand48() % p_sys->i_width[Y_PLANE];
|
||||
ps_dust->i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
|
||||
|
||||
ps_dust->i_stop_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t)vlc_mrand48() % HAIR_DURATION
|
||||
+ HAIR_DURATION / 2;
|
||||
|
||||
|
||||
ps_dust->i_x = MOD( (int32_t)vlc_mrand48(), p_sys->i_width[Y_PLANE] );
|
||||
ps_dust->i_y = MOD( (int32_t)vlc_mrand48(), p_sys->i_height[Y_PLANE] );
|
||||
|
||||
ps_dust->i_stop_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t)vlc_mrand48() % DUST_DURATION
|
||||
+ DUST_DURATION / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dust addition
|
||||
* smaller than blotch but will remain on the screen for long time
|
||||
*/
|
||||
int oldmovie_lens_dust_effect( filter_t *p_filter, picture_t *p_pic_out ) {
|
||||
filter_sys_t *p_sys = p_filter->p_sys;
|
||||
|
||||
/* generate new dust */
|
||||
if ( p_sys->i_dust_trigger <= p_sys->i_cur_time ) {
|
||||
for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
|
||||
if ( p_sys->p_dust[i_d] == NULL ) {
|
||||
/* allocate data */
|
||||
p_sys->p_dust[i_d] = calloc( 1, sizeof(dust_t) );
|
||||
if ( unlikely( !p_sys->p_dust[i_d] ) )
|
||||
return VLC_ENOMEM;
|
||||
|
||||
/* set random parameters */
|
||||
oldmovie_define_dust_location( p_filter, p_sys->p_dust[i_d] );
|
||||
p_sys->p_dust[i_d]->i_width = MOD( (int32_t)vlc_mrand48(), 5 ) + 1;
|
||||
p_sys->p_dust[i_d]->i_intensity = (unsigned)vlc_mrand48() % 30 + 30;
|
||||
|
||||
break;
|
||||
}
|
||||
p_sys->i_dust_trigger = p_sys->i_cur_time
|
||||
+ (uint64_t)vlc_mrand48() % DUST_GENERATOR_PERIOD
|
||||
+ DUST_GENERATOR_PERIOD / 2;
|
||||
}
|
||||
|
||||
/* manage and apply current dust */
|
||||
for ( uint32_t i_d = 0; i_d < MAX_DUST; i_d++ )
|
||||
if ( p_sys->p_dust[i_d] ) {
|
||||
/* remove outdated ones */
|
||||
if ( p_sys->p_dust[i_d]->i_stop_trigger <= p_sys->i_cur_time ) {
|
||||
/* select between moving or removing dust */
|
||||
if ( (unsigned)vlc_mrand48() % 2 == 0 )
|
||||
/* move dust */
|
||||
oldmovie_define_dust_location( p_filter, p_sys->p_dust[i_d] );
|
||||
else {
|
||||
/* remove dust */
|
||||
FREENULL( p_sys->p_dust[i_d] );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* draw dust */
|
||||
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++ )
|
||||
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++ )
|
||||
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 )
|
||||
CHECK_N_DARKEN_PIXEL( i_x + p_sys->p_dust[i_d]->i_x,
|
||||
i_y + p_sys->p_dust[i_d]->i_y,
|
||||
p_sys->p_dust[i_d]->i_intensity,
|
||||
&p_pic_out->p[Y_PLANE] );
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
|
@ -1109,6 +1109,7 @@ modules/video_filter/mosaic.c
|
|||
modules/video_filter/mosaic.h
|
||||
modules/video_filter/motionblur.c
|
||||
modules/video_filter/motiondetect.c
|
||||
modules/video_filter/oldmovie.c
|
||||
modules/video_filter/opencv_example.cpp
|
||||
modules/video_filter/opencv_wrapper.c
|
||||
modules/video_filter/posterize.c
|
||||
|
|
Loading…
Reference in New Issue