winamp/Src/Wasabi/api/skin/widgets/fx_dmove.cpp

409 lines
10 KiB
C++

#include <precomp.h>
#include <tataki/blending/blending.h>
#include "fx_dmove.h"
#include <api/skin/widgets/layer.h>
#include <math.h>
#define M_PI 3.14159265358979323846
FxDynamicMove::FxDynamicMove()
: fx_canvas(4,4)
{
need_flush=false;
cache_w=cache_h=4;
m_wmul=0;
m_tab=0;
last_m_tab=0;
m_lastxres=m_lastyres=m_lastpitch=0;
m_xres=16;
m_yres=16;
last_w=last_h=-1;
inited=0;
subpixel=1;
rectcoords=0;
blend=0;
wrap=0;
need_alpha = 1;
alpha_table = (double *)MALLOC((m_xres+1)*(m_yres+1)*sizeof(double));
alpha_once= 0;
can_cache = 1;
m_lastw = 0;
m_lasth = 0;
m_tab_size = 0;
}
FxDynamicMove::~FxDynamicMove()
{
FREE(m_wmul);
FREE(m_tab);
FREE(last_m_tab);
FREE(alpha_table);
}
int FxDynamicMove::render(Layer *l, int _w, int _h, int *input, int tw, int th, int twpitch)
{
double var_x;
double var_y;
double var_d;
double var_r;
/*if (fx_canvas) {
HDC dc;
dc = GetDC(NULL);
BitBlt(dc, 0, 0, w, h, fx_canvas->getHDC(),0, 0, SRCCOPY);
ReleaseDC(NULL, dc);
}*/
prepareCanvas(_w, _h);
int w_adj=(tw-2)<<16;
int h_adj=(th-2)<<16;
int dowrap=wrap;
int XRES=m_xres+1;
int YRES=m_yres+1;
int ignore_last_compare=0;
if (XRES > _w) XRES=_w;
if (YRES > _h) YRES=_h;
if (XRES < 2) XRES=2;
if (XRES > 256) XRES=256;
if (YRES < 2) YRES=2;
if (YRES > 256) YRES=256;
if (need_flush || m_lasth != th || m_lastpitch != twpitch || m_lastw != tw || !m_tab || !m_wmul || m_lastxres != XRES || m_lastyres != YRES)
{
int y;
m_lastxres = XRES;
m_lastyres = YRES;
m_lastw=tw;
m_lasth=th;
m_lastpitch=twpitch;
FREE(m_wmul);
m_wmul=(int*)MALLOC(sizeof(int)*th);
for (y = 0; y < th; y ++) m_wmul[y]=y*twpitch;
FREE(m_tab);
FREE(last_m_tab);
m_tab_size = (XRES*YRES*3 + XRES*6 + 6)*sizeof(int);
m_tab=(int*)MALLOC(m_tab_size);
last_m_tab=(int*)MALLOC(m_tab_size);
ignore_last_compare=1;
}
need_flush=false;
int isblend=blend;
int issub=subpixel;
if (!issub)
{
w_adj=(tw-1)<<16;
h_adj=(th-1)<<16;
}
if (w_adj<0) w_adj=0;
if (h_adj<0) h_adj=0;
{
int x;
int y;
int *tabptr=m_tab;
double xsc=2.0/tw,ysc=2.0/th;
double dw2=((double)tw*32768.0);
double dh2=((double)th*32768.0);
double max_screen_d=sqrt((double)(tw*tw+th*th))*0.5;
double divmax_d=1.0/max_screen_d;
max_screen_d *= 65536.0;
double _var_alpha = 0.50;
int yc_pos, yc_dpos, xc_pos, xc_dpos;
yc_pos=0;
xc_dpos = (tw<<16)/(XRES-1);
yc_dpos = (th<<16)/(YRES-1);
for (y = 0; y < YRES; y ++)
{
xc_pos=0;
for (x = 0; x < XRES; x ++)
{
double xd,yd;
xd=((double)xc_pos-dw2)*(1.0/65536.0);
yd=((double)yc_pos-dh2)*(1.0/65536.0);
xc_pos+=xc_dpos;
var_x=xd*xsc;
var_y=yd*ysc;
var_d=sqrt(xd*xd+yd*yd)*divmax_d;
var_r=atan2(yd,xd) + M_PI*0.5;
double _var_r=var_r, _var_d=var_d;
if (isblend && need_alpha) {
_var_alpha = l->fx_onGetPixelA(var_r, var_d, var_x, var_y);
alpha_table[y*YRES+x] = _var_alpha;
} else if (isblend && !need_alpha) {
_var_alpha = alpha_table[y*YRES+x];
}
if (!rectcoords) {
double t_var_r = l->fx_onGetPixelR(var_r, var_d, var_x, var_y);
_var_d = l->fx_onGetPixelD(var_r, var_d, var_x, var_y);
_var_r = t_var_r;
}
double _var_x=var_x, _var_y=var_y;
if (rectcoords) {
double t_var_x = l->fx_onGetPixelX(var_r, var_d, var_x, var_y);
_var_y = l->fx_onGetPixelY(var_r, var_d, var_x, var_y);
_var_x = t_var_x;
}
int tmp1,tmp2,tmp3;
if (!rectcoords)
{
_var_d *= max_screen_d;
_var_r -= M_PI*0.5;
tmp1=(int) (dw2 + cos(_var_r) * _var_d);
tmp2=(int) (dh2 + sin(_var_r) * _var_d);
}
else
{
tmp1=(int) ((_var_x+1.0)*dw2);
tmp2=(int) ((_var_y+1.0)*dh2);
}
if (!dowrap)
{
if (tmp1 < 0) tmp1=0;
if (tmp1 > w_adj) tmp1=w_adj;
if (tmp2 < 0) tmp2=0;
if (tmp2 > h_adj) tmp2=h_adj;
}
*tabptr++ = tmp1;
*tabptr++ = tmp2;
tmp3 = (int)(_var_alpha*65536.0*256.0);
if (tmp3 < 0) tmp3=0;
if (tmp3 > 0xff0000) tmp3=0xff0000;
*tabptr++=tmp3;
}
yc_pos+=yc_dpos;
}
if (alpha_once)
need_alpha=0;
}
if (can_cache && !ignore_last_compare && !MEMCMP(m_tab, last_m_tab, m_tab_size)) {
// cached, do nothing
// DebugString("cache hit!\n");
} else {
MEMCPY(last_m_tab, m_tab, m_tab_size);
// yay, the table is generated. now we do a fixed point
// interpolation of the whole thing and pray.
{
int *interptab=m_tab+XRES*YRES*3;
int *rdtab=m_tab;
unsigned int *in=(unsigned int *)input;
unsigned int *blendin=(unsigned int *)input;
unsigned int *out=(unsigned int *)fx_canvas.getBits();
int yseek=1;
int xc_dpos, yc_pos=0, yc_dpos;
xc_dpos=(tw<<16)/(XRES-1);
yc_dpos=(th<<16)/(YRES-1);
int lypos=0;
int yl=_h;
while (yl>0)
{
yc_pos+=yc_dpos;
yseek=(yc_pos>>16)-lypos;
if (!yseek) return 0;
lypos=yc_pos>>16;
int l=XRES;
int *stab=interptab;
int xr3=XRES*3;
while (l--)
{
int tmp1, tmp2,tmp3;
tmp1=rdtab[0];
tmp2=rdtab[1];
tmp3=rdtab[2];
stab[0]=tmp1;
stab[1]=tmp2;
stab[2]=(rdtab[xr3]-tmp1)/yseek;
stab[3]=(rdtab[xr3+1]-tmp2)/yseek;
stab[4]=tmp3;
stab[5]=(rdtab[xr3+2]-tmp3)/yseek;
rdtab+=3;
stab+=6;
}
if (yseek > yl) yseek=yl;
yl-=yseek;
if (yseek > 0) while (yseek--)
{
int d_x;
int d_y;
int d_a;
int ap;
int seek;
int *seektab=interptab;
int xp,yp;
int l=_w;
int lpos=0;
int xc_pos=0;
while (l>0)
{
xc_pos+=xc_dpos;
seek=(xc_pos>>16)-lpos;
if (!seek)
{
#ifndef NO_MMX
Blenders::BLEND_MMX_END();
#endif
return 0;
}
lpos=xc_pos>>16;
xp=seektab[0];
yp=seektab[1];
ap=seektab[4];
d_a=(seektab[10]-ap)/(seek);
d_x=(seektab[6]-xp)/(seek);
d_y=(seektab[7]-yp)/(seek);
seektab[0] += seektab[2];
seektab[1] += seektab[3];
seektab[4] += seektab[5];
seektab+=6;
if (seek>l) seek=l;
l-=seek;
if (seek>0)
{
// normal loop
#define NORMAL_LOOP(Z) while (seek--) { Z; xp+=d_x; yp+=d_y; }
// wrapping loop
#define WRAPPING_LOOPS(Z) \
if (d_x <= 0 && d_y <= 0) NORMAL_LOOP(if (xp < 0) xp += w_adj; if (yp < 0) yp += h_adj; Z) \
else if (d_x <= 0) NORMAL_LOOP(if (xp < 0) xp += w_adj; if (yp >= h_adj) yp-=h_adj; Z) \
else if (d_y <= 0) NORMAL_LOOP(if (xp >= w_adj) xp-=w_adj; if (yp < 0) yp += h_adj; Z) \
else NORMAL_LOOP(if (xp >= w_adj) xp-=w_adj; if (yp >= h_adj) yp-=h_adj; Z)
// this is uber gay. J1, D4, L or J1_MMX, D4_MMX, L_MMX
#define LOOPS(DO,J1,D4,L) \
if ((isblend&2) && issub) DO(*out++=Blenders::BLEND_AD##J1(Blenders::BLEN##D4(in+(xp>>16)+m_wmul[yp>>16],twpitch,xp,yp),*blendin++,ap>>16); ap+=d_a) \
else if (isblend&2) DO(*out++=Blenders::BLEND_AD##J1(in[(xp>>16)+m_wmul[yp>>16]],*blendin++,ap>>16); ap+=d_a) \
else if (isblend && issub) DO(*out++=Blenders::BLEND_MU##L(Blenders::BLEN##D4(in+(xp>>16)+m_wmul[yp>>16],twpitch,xp,yp),ap>>16); ap+=d_a) \
else if (isblend) DO(*out++=Blenders::BLEND_MU##L(in[(xp>>16)+m_wmul[yp>>16]],ap>>16); ap+=d_a) \
else if (issub) DO(*out++=Blenders::BLEN##D4(in+(xp>>16)+m_wmul[yp>>16],twpitch,xp,yp)) \
else DO(*out++=in[(xp>>16)+m_wmul[yp>>16]])
if (!dowrap)
{
#ifndef NO_MMX
if (Blenders::MMX_AVAILABLE())
{
LOOPS(NORMAL_LOOP,J1_MMX,D4_MMX,L_MMX)
}
else
#endif
{
LOOPS(NORMAL_LOOP,J1,D4,L)
}
}
else // dowrap
{
xp %= w_adj+1; yp %= h_adj+1;
if (xp < 0) xp+=w_adj; if (yp < 0) yp+=h_adj;
if (d_x < -w_adj || d_x > w_adj) d_x=0; if (d_y < -h_adj || d_y > h_adj) d_y=0;
#ifndef NO_MMX
if (Blenders::MMX_AVAILABLE())
{
LOOPS(WRAPPING_LOOPS,J1_MMX,D4_MMX,L_MMX)
}
else
#endif
{
LOOPS(WRAPPING_LOOPS,J1,D4,L)
}
}
}
}
blendin+=twpitch-_w;
// adjust final (rightmost elem) part of seektab
seektab[0] += seektab[2];
seektab[1] += seektab[3];
seektab[4] += seektab[5];
}
}
}
}
/*HDC dc = GetDC(NULL);
BitBlt(dc, 0, 0, w, h, fx_canvas->getHDC(),0, 0, SRCCOPY);
ReleaseDC(NULL, dc);*/
#ifndef NO_MMX
Blenders::BLEND_MMX_END();
#endif
return 0;
};
void FxDynamicMove::setWrap(int i) {
wrap = i;
}
void FxDynamicMove::setRect(int i) {
rectcoords = i;
}
void FxDynamicMove::setBilinear(int i) {
subpixel = i;
}
void FxDynamicMove::setAlphaMode(int i) {
blend = i;
}
void FxDynamicMove::setAlphaOnce(int i) {
alpha_once = i;
}
void FxDynamicMove::setGridSize(int x, int y) {
m_xres = x;
m_yres = y;
FREE(alpha_table);
alpha_table = (double *)MALLOC((x+1)*(y+1)*sizeof(double));
need_alpha = 1;
}
BltCanvas *FxDynamicMove::getBltCanvas()
{
return &fx_canvas;
}
void FxDynamicMove::prepareCanvas(int w, int h) {
if (w != last_w || h != last_h)
{
if (cache_w < w || cache_h < h)
{
cache_w = max(cache_w, w);
cache_h = max(cache_h, h);
fx_canvas.DestructiveResize(cache_w, cache_h, 32);
}
last_w = w;
last_h = h;
}
}
void FxDynamicMove::setCanCache(int i) {
can_cache = i;
}
void FxDynamicMove::flushCache()
{
need_flush=true;
}