Files
veejay/veejay-current/veejay-server/libvje/effects/water.c
2015-06-13 23:54:51 +02:00

648 lines
15 KiB
C

/* EffecTV - Realtime Digital Video Effektor
* Copyright (C) 2001-2003 FUKUCHI Kentaro
*
* RippleTV - Water ripple effect
* Copyright (C) 2001 - 2002 FUKUCHI Kentaro
*
* ported to Linux VeeJay by:
* Copyright(C)2002 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 <config.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <libvjmem/vjmem.h>
#include "rippletv.h"
#include "softblur.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include "common.h"
typedef struct {
uint8_t *ripple_data[4];
uint8_t *diff_img;
int stat;
signed char *vtable;
int *map;
int *map1;
int *map2;
int *map3;
int map_h;
int map_w;
int have_img;
int sqrtable[256];
int point;
int impact;
int last_fresh_rate;
int loopnum;
int lastmode;
unsigned int wfastrand_val;
} water_t;
/*static uint8_t *ripple_data[3];
static int stat;
static signed char *vtable;
static int *map;
static int *map1, *map2, *map3;
static int map_h, map_w;
static uint8_t *diff_img = NULL;
static int have_img = 0;
static int sqrtable[256];
static const int point = 16;
static const int impact = 2;
//static const int loopnum = 2;
static int bgIsSet = 0;
*/
/* from EffecTV:
* fastrand - fast fake random number generator
* Warning: The low-order bits of numbers generated by fastrand()
* are bad as random numbers. For example, fastrand()%4
* generates 1,2,3,0,1,2,3,0...
* You should use high-order bits.
*/
unsigned int wfastrand(water_t *w)
{
return (w->wfastrand_val=w->wfastrand_val*1103515245+12345);
}
static void setTable(water_t *w)
{
int i;
for(i=0; i<128; i++) {
w->sqrtable[i] = i*i;
}
for(i=1; i<=128; i++) {
w->sqrtable[256-i] = -i*i;
}
}
//easy/calm: 10,2,32
//flimmerin: 10,2,3
//flowing: 10,1,29
//p2 = wave speed
//
vj_effect *water_init(int width, int height)
{
vj_effect *ve = (vj_effect *) vj_calloc(sizeof(vj_effect));
ve->num_params = 5;
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] = 1;
ve->limits[1][0] = 3600;
ve->limits[0][1] = 1;
ve->limits[1][1] = 16;
ve->limits[0][2] = 1;
ve->limits[1][2] = 31; //number of waves
ve->limits[0][3] = 0;
ve->limits[1][3] = 6; // mode
ve->limits[0][4] = 0;
ve->limits[1][4] = 255; // threshold
ve->defaults[0] = 10;
ve->defaults[1] = 1;
ve->defaults[2] = 10;
ve->defaults[3] = 1;
ve->defaults[4] = 45;
ve->description = "Water ripples";
ve->sub_format = 0;
ve->extra_frame = 1;
ve->has_user = 1;
ve->user_data = NULL;
ve->param_description= vje_build_param_list(ve->num_params, "Refresh Frequency",
"Wavespeed", "Decay", "Mode", "Threshold (motion)");
return ve;
}
#define HIS_LEN (8*25)
#define HIS_DEFAULT 2
static uint32_t histogram_[HIS_LEN];
static int n__ = 0;
static uint32_t keyv_ = 0;
static uint32_t keyp_ = 0;
int water_malloc(void **d, int width, int height)
{
*d = (void*) vj_calloc(sizeof(water_t));
water_t *w = (water_t*) *d;
w->ripple_data[0] = (uint8_t*)vj_calloc(sizeof(uint8_t) * RUP8(width * height));
if(!w->ripple_data[0]) return 0;
w->diff_img = (uint8_t*)vj_calloc(sizeof(uint8_t) * RUP8(width * height * 2));
if(!w->diff_img) return 0;
w->map_h = height / 2 + 1;
w->map_w = width / 2 + 1;
w->map = (int*) vj_calloc (sizeof(int) * RUP8( w->map_h * w->map_w * 3));
if(!w->map) return 0;
w->vtable = (signed char*) vj_calloc( sizeof(signed char) * RUP8(w->map_w * w->map_h * 2));
if(!w->vtable) return 0;
w->map3 = w->map + w->map_w * w->map_h * 2;
setTable(w);
w->map1 = w->map;
w->map2 = w->map + w->map_h*w->map_w;
w->stat = 1;
w->point = 16;
w->impact = 2;
w->loopnum = 2;
return 1;
}
void water_free(void *ud) {
water_t *w = (water_t*) ud;
if(w) {
if(w->ripple_data[0]) free(w->ripple_data[0]);
if(w->map) free(w->map);
if(w->diff_img) free(w->diff_img);
if(w->vtable) free(w->vtable);
free(w);
}
w = NULL;
}
static inline void drop(water_t *w,int power)
{
int x, y;
int *p, *q;
x = wfastrand(w)%(w->map_w-4)+2;
y = wfastrand(w)%(w->map_h-4)+2;
p = w->map1 + y*w->map_w + x;
q = w->map2 + y*w->map_w + x;
*p = power;
*q = power;
*(p-w->map_w) = *(p-1) = *(p+1) = *(p+w->map_w) = power/2;
*(p-w->map_w-1) = *(p-w->map_w+1) = *(p+w->map_w-1) = *(p+w->map_w+1) = power/4;
*(q-w->map_w) = *(q-1) = *(q+1) = *(q+w->map_w) = power/2;
*(q-w->map_w-1) = *(q-w->map_w+1) = *(q+w->map_w-1) = *(p+w->map_w+1) = power/4;
}
static void drawmotionframe( VJFrame *f , water_t *w )
{
int strides[4] = { 0, f->uv_len,f->uv_len, 0 };
vj_frame_clear( f->data, strides, 128 );
vj_frame_clear( f->data, strides, 128 );
vj_frame_copy1( w->diff_img, f->data[0], f->width * f->height );
// veejay_memcpy( f->data[0], w->diff_img, f->width * f->height );
}
static int globalactivity(VJFrame *f2, water_t *w, int in)
{
int len = (f2->width * f2->height)/4;
uint32_t sum = 0,min=0xffff,max=0;
uint64_t activity_level1 = 0;
uint64_t activity_level2 = 0;
uint64_t activity_level3 = 0;
uint64_t activity_level4 = 0;
uint8_t *binary_img = w->diff_img;
int i = 0;
for( i = 0; i < len; i += 4 )
{
activity_level1 += binary_img[i];
activity_level2 += binary_img[i+1];
activity_level3 += binary_img[i+2];
activity_level4 += binary_img[i+3];
}
uint32_t activity_level = ( (activity_level1>>8) + (activity_level2>>8) + (activity_level3>>8) + (activity_level4>>8));
int current_his_len = 8;
histogram_[ (n__%current_his_len) ] = activity_level;
for( i = 0; i < current_his_len; i ++ )
{
sum += histogram_[i];
if(histogram_[i] > max ) max = histogram_[i];
if(histogram_[i] < min ) min = histogram_[i];
}
if( (n__ % current_his_len)==0 )
{
keyp_ = keyv_;
keyv_ = (sum > 0 ? (sum/current_his_len):0 );
}
if( n__ <= 1 )
return in;
int tmp = (( n__ - 1) % current_his_len) + 1;
float q = 1.0f/(float) current_his_len * tmp;
float diff = (float) keyv_ - (float) keyp_;
float pu = keyp_ + ( q* diff);
float wu = 1.0f/31;
float pw = wu * pu;
int res = (30 * pw);
if( res < 1 )
return 1;
return res;
}
static void motiondetect(VJFrame *f, VJFrame *f2, int threshold, water_t *w)
{
uint8_t *bg = w->diff_img + (f->width * f->height);
uint8_t *in = f2->data[0];
if(!w->have_img)
{
//softblur_apply( f2,f->width,f->height,0);
//veejay_memcpy(bg, f2->data[0], f->width * f->height );
vj_frame_copy1( f2->data[0],bg, f->width * f->height );
w->have_img = 1;
return;
}
int i,len= f->width * f->height;
uint8_t pp1;
for(i = 0; i < len ; i ++ ) {
pp1 = abs(bg[i] - in[i]);
if(pp1 > threshold ) {
w->diff_img[i] = pp1;
} else {
w->diff_img[i] = 0;
// (w->diff_img[i] + 0)>>1;
}
}
int *p = w->map1 + w->map_w + 1;
int *q = w->map2 + w->map_w + 1;
int width = f->width;
int x,y,h;
uint8_t *d = w->diff_img + width + 2;
for( y = w->map_h - 2; y > 0 ; y -- ) {
for( x = w->map_w - 2 ; x > 0 ; x -- ) {
h = (int) *d + (int) *(d+1) + (int) *(d+width) + (int) *(d+width+1);
if(h>0) {
*p = h << ( w->point + w->impact - 8 );
*q = *p;
}
p ++; q ++;
d += 2;
}
d += width + 2;
p += 2;
q += 2;
}
}
static void motiondetect2(VJFrame *f, VJFrame *f2, int threshold, water_t *w)
{
uint8_t *bg = w->diff_img + (f->width * f->height);
uint8_t *in = f2->data[0];
if(!w->have_img)
{
softblur_apply( f2,f->width,f->height,0);
// veejay_memcpy(bg, f2->data[0], f->width * f->height );
vj_frame_copy1( f2->data[0], bg, f->width * f->height );
w->have_img = 1;
return;
}
int i,len= f->width * f->height;
uint8_t pp1;
for(i = 0; i < len ; i ++ ) {
pp1 = abs(bg[i] - in[i]);
if(pp1 > threshold ) {
w->diff_img[i] = 0xff-pp1;
} else {
w->diff_img[i] = 0;
// (w->diff_img[i] + 0)>>1;
}
}
int *p = w->map1 + w->map_w + 1;
int *q = w->map2 + w->map_w + 1;
int width = f->width;
int x,y,h;
uint8_t *d = w->diff_img + width + 2;
for( y = w->map_h - 2; y > 0 ; y -- ) {
for( x = w->map_w - 2 ; x > 0 ; x -- ) {
h = (int) *d + (int) *(d+1) + (int) *(d+width) + (int) *(d+width+1);
if(h>0) {
*p = h << ( w->point + w->impact - 8 );
*q = *p;
}
p ++; q ++;
d += 2;
}
d += width + 2;
p += 2;
q += 2;
}
}
static void motiondetect3(VJFrame *f, VJFrame *f2, int threshold, water_t *w)
{
uint8_t *bg = w->diff_img + (f->width * f->height);
uint8_t *in = f2->data[0];
if(!w->have_img)
{
softblur_apply( f2,f->width,f->height,0);
// veejay_memcpy(bg, f2->data[0], f->width * f->height );
vj_frame_copy1( f2->data[0], bg, f->width * f->height );
w->have_img = 1;
return;
}
int i,len= f->width * f->height;
uint8_t pp1;
for(i = 0; i < len ; i ++ ) {
pp1 = abs(bg[i] - in[i]);
if(pp1 < threshold ) {
w->diff_img[i] = pp1;
} else {
w->diff_img[i] = 0;
// (w->diff_img[i] + 0)>>1;
}
}
int *p = w->map1 + w->map_w + 1;
int *q = w->map2 + w->map_w + 1;
int width = f->width;
int x,y,h;
uint8_t *d = w->diff_img + width + 2;
for( y = w->map_h - 2; y > 0 ; y -- ) {
for( x = w->map_w - 2 ; x > 0 ; x -- ) {
h = (int) *d + (int) *(d+1) + (int) *(d+width) + (int) *(d+width+1);
if(h>0) {
*p = h << ( w->point + w->impact - 8 );
*q = *p;
}
p ++; q ++;
d += 2;
}
d += width + 2;
p += 2;
q += 2;
}
}
static void raindrop(water_t *w)
{
static int period = 0;
static int rain_stat = 0;
static unsigned int drop_prob = 0;
static int drop_prob_increment = 0;
static int drops_per_frame_max = 0;
static int drops_per_frame = 0;
static int drop_power = 0;
int i;
if(period == 0) {
switch(rain_stat) {
case 0:
period = (wfastrand(w)>>23)+100;
drop_prob = 0;
drop_prob_increment = 0x00ffffff/period;
drop_power = (-(wfastrand(w)>>28)-2)<<w->point;
drops_per_frame_max = 2<<(wfastrand(w)>>30); // 2,4,8 or 16
rain_stat = 1;
break;
case 1:
drop_prob = 0x00ffffff;
drops_per_frame = 1;
drop_prob_increment = 1;
period = (drops_per_frame_max - 1) * 16;
rain_stat = 2;
break;
case 2:
period = (wfastrand(w)>>22)+1000;
drop_prob_increment = 0;
rain_stat = 3;
break;
case 3:
period = (drops_per_frame_max - 1) * 16;
drop_prob_increment = -1;
rain_stat = 4;
break;
case 4:
period = (wfastrand(w)>>24)+60;
drop_prob_increment = -(drop_prob/period);
rain_stat = 5;
break;
case 5:
default:
period = (wfastrand(w)>>23)+500;
drop_prob = 0;
rain_stat = 0;
break;
}
}
switch(rain_stat) {
default:
case 0:
break;
case 1:
case 5:
if((wfastrand(w)>>8)<drop_prob) {
drop(w,drop_power);
}
drop_prob += drop_prob_increment;
break;
case 2:
case 3:
case 4:
for(i=drops_per_frame/16; i>0; i--) {
drop(w,drop_power);
}
drops_per_frame += drop_prob_increment;
break;
}
period--;
}
void water_apply(void *user_data, VJFrame *frame, VJFrame *frame2, int width, int height, int fresh_rate, int loopnum, int decay, int mode, int threshold )
{
int x, y, i;
int dx, dy;
int h, v;
int wi, hi;
int *p, *q, *r;
signed char *vp;
uint8_t *src,*dest;
const int len = frame->len;
water_t *w = (water_t*) user_data;
if(w->last_fresh_rate != fresh_rate)
{
w->last_fresh_rate = fresh_rate;
veejay_memset( w->map, 0, (w->map_h*w->map_w*2*sizeof(int)));
}
if(w->lastmode != mode )
{
veejay_memset( w->map, 0, (w->map_h*w->map_w*2*sizeof(int)));
w->have_img = 0;
w->lastmode = mode;
}
vj_frame_copy1( frame->data[0], w->ripple_data[0],len);
dest = frame->data[0];
src = w->ripple_data[0];
w->loopnum = loopnum;
switch(mode) {
case 0: raindrop(w); w->have_img = 0; break;
case 1:
motiondetect(frame,frame2,threshold,w);
drawmotionframe(frame,w);
return;
case 2: motiondetect(frame,frame2,threshold,w);
break;
case 3:
motiondetect2(frame,frame2,threshold,w);
drawmotionframe(frame,w);
return;
break;
case 4:
motiondetect2(frame,frame2,threshold,w);
break;
case 5:
motiondetect3(frame,frame2,threshold,w);
drawmotionframe(frame,w);
return;
case 6:
motiondetect3(frame,frame2,threshold,w);
break;
default:
break;
}
/* simulate surface wave */
wi = w->map_w;
hi = w->map_h;
/* This function is called only 30 times per second. To increase a speed
* of wave, iterates this loop several times. */
for(i=w->loopnum; i>0; i--) {
/* wave simulation */
p = w->map1 + wi + 1;
q = w->map2 + wi + 1;
r = w->map3 + wi + 1;
for(y=hi-2; y>0; y--) {
for(x=wi-2; x>0; x--) {
h = *(p-wi-1) + *(p-wi+1) + *(p+wi-1) + *(p+wi+1)
+ *(p-wi) + *(p-1) + *(p+1) + *(p+wi) - (*p)*9;
h = h >> 3;
v = *p - *q;
v += h - (v >> decay);
*r = v + *p;
p++;
q++;
r++;
}
p += 2;
q += 2;
r += 2;
}
/* low pass filter */
p = w->map3 + wi + 1;
q = w->map2 + wi + 1;
for(y=hi-2; y>0; y--) {
for(x=wi-2; x>0; x--) {
h = *(p-wi) + *(p-1) + *(p+1) + *(p+wi) + (*p)*60;
*q = h >> 6;
p++;
q++;
}
p+=2;
q+=2;
}
p = w->map1;
w->map1 = w->map2;
w->map2 = p;
}
vp = w->vtable;
p = w->map1;
for(y=hi-1; y>0; y--) {
for(x=wi-1; x>0; x--) {
/* difference of the height between two voxel. They are twiced to
* emphasise the wave. */
vp[0] = w->sqrtable[((p[0] - p[1])>>(w->point-1))&0xff];
vp[1] = w->sqrtable[((p[0] - p[wi])>>(w->point-1))&0xff];
p++;
vp+=2;
}
p++;
vp+=2;
}
hi = height;
wi = width;
vp = w->vtable;
for(y=0; y<hi; y+=2) {
for(x=0; x<wi; x+=2) {
h = (int)vp[0];
v = (int)vp[1];
dx = x + h;
dy = y + v;
if(dx<0) dx=0;
if(dy<0) dy=0;
if(dx>=wi) dx=wi-1;
if(dy>=hi) dy=hi-1;
dest[0] = src[dy*wi+dx];
i = dx;
dx = x + 1 + (h+(int)vp[2])/2;
if(dx<0) dx=0;
if(dx>=wi) dx=wi-1;
dest[1] = src[dy*wi+dx];
dy = y + 1 + (v+(int)vp[w->map_w*2+1])/2;
if(dy<0) dy=0;
if(dy>=hi) dy=h-1;
dest[wi] = src[dy*wi+i];
dest[wi+1] = src[dy*wi+dx];
dest+=2;
vp+=2;
}
dest += wi;
vp += 2;
}
}