Files
veejay/veejay-current/veejay-server/libvje/effects/shapewipe.c

367 lines
9.9 KiB
C

/*
* Linux VeeJay
*
* Copyright(C)2019 Niels Elburg <nwelburg@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 , USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#include <libavutil/avutil.h>
#include "../effects/common.h"
#include "../libel/pixbuf.h"
#include <veejaycore/avcommon.h>
#include <veejaycore/vjmem.h>
#include <veejaycore/vj-msg.h>
#include "shapewipe.h"
#define MAX_NUMBER_OF_SHAPES 10000 // the amount of shapes this FX can hold
static int is_img( const char *file )
{
if( strstr( file, ".png" ) || strstr( file, ".PNG" ) )
return 1;
if( strstr( file, ".pgm" ) || strstr( file, ".PGM" ) )
return 1;
if( strstr( file, ".tif" ) || strstr( file, ".TIF" ) || strstr( file, ".tiff" ) || strstr( file, ".TIFF" ) )
return 1;
return 0;
}
static int find_shape_file(char *path, char **shapelist, int *shapeidx, int maxshapes )
{
if(!path)
return 0;
struct stat l;
veejay_memset(&l, 0, sizeof(struct stat));
if( lstat( path, &l ) < 0 )
return 0;
if( S_ISLNK(l.st_mode) ) {
veejay_memset(&l,0,sizeof(struct stat));
stat(path, &l );
}
if( S_ISDIR(l.st_mode)) {
return 1;
}
if( S_ISREG(l.st_mode)) {
if(is_img(path)) {
if(*shapeidx < maxshapes) {
shapelist[ *shapeidx ] = strdup(path);
*shapeidx = *shapeidx + 1;
}
}
}
return 0;
}
static int find_shapes(char *path, char **shapelist, int *shapeidx, int maxshapes)
{
struct dirent **files;
int n = scandir(path, &files, NULL, alphasort);
if( n < 0 )
return 0;
char tmp[2048];
while( n -- ) {
snprintf(tmp,sizeof(tmp), "%s/%s", path, files[n]->d_name );
if(strcmp( files[n]->d_name, "." ) != 0 && strcmp( files[n]->d_name, ".." ) != 0 ) {
if( find_shape_file(tmp, shapelist, shapeidx, maxshapes ) )
find_shapes(tmp, shapelist, shapeidx, maxshapes );
}
free(files[n]);
}
free(files);
return 1;
}
static void load_shapes(char **shapelist, int *shapeidx, int maxshapes)
{
char *home = getenv("HOME");
char path[2048];
snprintf(path, sizeof(path), "%s/.veejay/shapes", home );
find_shapes( path, shapelist, shapeidx, maxshapes );
find_shapes( "/usr/local/share/veejay/shapes", shapelist, shapeidx, maxshapes );
find_shapes( "/usr/share/veejay/shapes", shapelist, shapeidx, maxshapes );
}
typedef struct {
char **shapelist;
int shapeidx;
int currentshape; // -1
void *selected_shape;
int shape_min;
int shape_max;
int shape_completed;
} shape_t;
static shape_t *init_shape_loader()
{
shape_t *s = (shape_t*) vj_calloc(sizeof(shape_t));
if(!s) {
return NULL;
}
int maxshapes = MAX_NUMBER_OF_SHAPES;
s->shapelist = (char**) vj_calloc(sizeof(char*) * maxshapes );
if(!s->shapelist) {
free(s);
return NULL;
}
s->currentshape = -1;
load_shapes( s->shapelist, &(s->shapeidx), maxshapes);
return s;
}
static void free_shape_loader(shape_t *s)
{
int i;
for( i = 0; i < MAX_NUMBER_OF_SHAPES; i ++ ) {
if(s->shapelist[i]) {
free(s->shapelist[i]);
}
}
free(s->shapelist);
free(s);
}
static void *change_shape(shape_t *s, void *oldshape, int shape, int w, int h)
{
if(oldshape)
vj_picture_cleanup( oldshape );
return vj_picture_open( s->shapelist[shape], w,h, PIX_FMT_GRAY8 );
}
static char **get_shapelist_hints(char **shapelist, int num )
{
char **result = (char**) vj_calloc(sizeof(char*) * num );
int i;
for( i = 0; i <= num; i ++ ) {
char *ptr = basename( shapelist[i] );
result[i] = strdup(ptr);
}
return result;
}
static void free_shapelist_hints(char **hints, int num )
{
int i;
for( i = 0; i <= num; i ++ ) {
free(hints[i]);
}
free(hints);
}
static void shape_find_min_max(uint8_t *data, const int len, int *min, int *max )
{
int a=256,b=0;
int i;
for( i = 0; i < len; i ++ ) {
if( data[i] > b )
b = data[i];
if( data[i] < a )
a = data[i];
}
*min = a;
*max = b;
}
static void shape_wipe_1( uint8_t *dst[4], uint8_t *src[4], uint8_t *pattern, const int len, const int threshold)
{
int i;
for( i = 0; i < len; i ++ )
{
if( pattern[i] < threshold)
{
dst[0][i] = src[0][i];
dst[1][i] = src[1][i];
dst[2][i] = src[2][i];
}
}
}
static void shape_wipe_2( uint8_t *dst[4], uint8_t *src[4], uint8_t *pattern, const int len, const int threshold)
{
int i;
for( i = 0; i < len; i ++ )
{
if( pattern[i] > threshold )
{
dst[0][i] = src[0][i];
dst[1][i] = src[1][i];
dst[2][i] = src[2][i];
}
}
}
int shapewipe_get_num_shapes(void *ptr)
{
shape_t *s = (shape_t*) ptr;
return s->shapeidx - 1;
}
int shapewipe_ready(void *ptr, int w, int h)
{
shape_t *s = (shape_t*) ptr;
return s->shape_completed;
}
vj_effect *shapewipe_init(int w, int h)
{
shape_t *s = init_shape_loader();
if(!s) {
return NULL;
}
vj_effect *ve = (vj_effect *) vj_calloc(sizeof(vj_effect));
ve->num_params = 4;
ve->defaults = (int *) vj_calloc(sizeof(int) * ve->num_params); /* default values */
ve->limits[0] = (int *) vj_calloc(sizeof(int) * ve->num_params); /* min */
ve->limits[1] = (int *) vj_calloc(sizeof(int) * ve->num_params); /* max */
ve->limits[0][0] = 0;
ve->limits[0][1] = 0;
ve->limits[1][1] = 256;
ve->limits[0][2] = 0;
ve->limits[1][2] = 1;
ve->limits[0][3] = 0;
ve->limits[1][3] = 1;
ve->defaults[0] = 0;
ve->defaults[1] = 0;
ve->defaults[2] = 0;
ve->defaults[3] = 1;
ve->description = "Shape Wipe";
ve->sub_format = 1;
ve->extra_frame = 1;
ve->parallel = 0;
ve->has_user = 0;
ve->param_description = vje_build_param_list( ve->num_params, "Shape", "Threshold", "Direction", "Automatic");
ve->hints = vje_init_value_hint_list( ve->num_params );
if(ve->limits[1][0] > 0) {
char **hints = get_shapelist_hints( s->shapelist, ve->limits[1][0] );
vje_build_value_hint_list_array( ve->hints, ve->limits[1][0], 0, hints );
free_shapelist_hints(hints, ve->limits[1][0]);
}
else {
veejay_msg(0, "You didn't put any shape transitions in $HOME/.veejay/shapes, I have nothing to do!");
}
vje_build_value_hint_list( ve->hints, ve->limits[1][2], 2, "White to Black", "Black to White" );
char *home = getenv("HOME");
veejay_msg(VEEJAY_MSG_INFO, "Put your shape transition files (png, pgm, tiff) in %s/.veejay/shapes", home );
ve->is_transition_ready_func = shapewipe_ready;
ve->limits[1][0] = s->shapeidx - 1;
veejay_msg(VEEJAY_MSG_INFO, "Loaded %d shape transitions from storage", s->shapeidx - 1);
free_shape_loader(s);
return ve;
}
void *shapewipe_malloc(int w, int h) {
return init_shape_loader();
}
void shapewipe_free(void *ptr) {
shape_t *s = (shape_t*) ptr;
if(s->selected_shape) {
vj_picture_cleanup( s->selected_shape );
}
free_shape_loader(s);
}
static int shapewipe_apply1(void *ptr, VJFrame *frame, VJFrame *frame2, double timecode, int shape, int threshold, int direction, int automatic)
{
shape_t *s = (shape_t*) ptr;
if( shape != s->currentshape) {
s->selected_shape = change_shape( s,s->selected_shape, shape, frame->width, frame->height );
if(s->selected_shape == NULL) {
veejay_msg(0, "Unable to read %s", s->shapelist[ shape ] );
return 0;
}
s->currentshape = shape;
VJFrame *tmp = vj_picture_get( s->selected_shape );
shape_find_min_max( tmp->data[0], tmp->len, &(s->shape_min), &(s->shape_max) );
}
VJFrame *src = vj_picture_get( s->selected_shape );
int auto_threshold = threshold;
int range = (s->shape_max - s->shape_min);
if(direction) {
if(automatic)
auto_threshold = (int) ( timecode * range ) + s->shape_min;
shape_wipe_1( frame->data, frame2->data, src->data[0], frame->len, auto_threshold );
if( auto_threshold >= s->shape_max )
return 1;
}
else {
if(automatic)
auto_threshold = (int) range - (timecode * range ) + s->shape_min;
shape_wipe_2( frame->data, frame2->data, src->data[0], frame->len, auto_threshold );
if( auto_threshold <= s->shape_min )
return 1;
}
return 0;
}
void shapewipe_apply( void *ptr, VJFrame *frame, VJFrame *frame2, int *args) {
double timecode = frame->timecode;
int shape = args[0];
int threshold = args[1];
int direction = args[2];
int automatic = args[3];
shape_t *s = (shape_t*) ptr;
s->shape_completed = shapewipe_apply1(ptr, frame,frame2,timecode,shape,threshold,direction,automatic);
}
int shapewipe_process( void *ptr, VJFrame *frame, VJFrame *frame2,double timecode, int shape, int threshold, int direction, int automatic) {
shape_t *s = (shape_t*) ptr;
s->shape_completed = shapewipe_apply1(ptr, frame,frame2,timecode,shape,threshold,direction,automatic);
return s->shape_completed;
}