Code:
struct particle
{
typedef enum {linear, square, log, cos, sin, cost} attractMode;
particle()
{
}
double x, y;
double mag;
double maxDist;
double fK;
attractMode mode;
};
struct partInfo
{
std::vector<particle> particles;
UINT w, h;
double fCenterX, fCenterY;
};
// our custom XForm.
BOOL CALLBACK ParticleXForm(const double wx, const double wy,
double *x, double *y,
const ISE_CALLBACK_DATA uUserData)
{
partInfo *pInfo = (partInfo*)uUserData;
double vx=0, vy=0;
for (std::vector<particle>::iterator it = pInfo->particles.begin();
it != pInfo->particles.end();
it++)
{
if ((*it).maxDist==0)
{
continue;
}
double offX=0, offY=0;
double dx = wx - (*it).x, dy = wy - (*it).y;
double fDist = sqrt(dx * dx + dy * dy);
double mag = 0;
switch ((*it).mode)
{
case particle::linear:
if (fDist <= (*it).maxDist)
mag = 1.0 - fDist / (*it).maxDist;
break;
// ... other modes removed to keep the sample short...
}
mag *= (*it).mag;
// new point lies this far out on the line
double fNewDist = mag;
// find the angle that the input points make with the +X axis
if (dx != 0)
{
double fAngle = atan((double)dy / (double)dx);
double nY = sin(fAngle) * fNewDist, nX = cos(fAngle) * fNewDist;
// handle negative quadrants
if (dx >= 0)
{
offX = nX; offY = nY;
}
else
{
offX = -nX; offY = -nY;
}
}
else
{
// on y axis
offX = 0;
if (dy < 0)
offY = -fNewDist;
else
offY = fNewDist ;
}
vx+=offX;
vy+=offY;
}
*x = wx + vx;
*y = wy + vy;
return TRUE;
}
and here's the main function:
Code:
#include "ISource.h"
#include "ISEffects.h"
#include <io.h>
#include <iostream>
#include "math.h"
...
void WarpIt()
{
bool bAVI = true;
int frame_rate = 20; // 20 per second
// the song is 4 minutes and 4 seconds long.
int totalFrames = frame_rate * (60 * 4 + 4);// 4:04 = 4880 frames;
//int totalFrames = frame_rate * 10; // 10 sec demo, useful for testing
char *lpFilename = "c:\\temp\\warp_out.avi";
if (_access(lpFilename, 0) == 0)
{
if (!DeleteFile(lpFilename))
{
AfxMessageBox("Can't delete target. (Is it open in WMP?");
return;
}
}
::CoInitialize(NULL);
PAVIFILE pfile = NULL;
PAVISTREAM ps = NULL, psCompressed = NULL;
// get a DC so we can draw frames as they're rendered
CClientDC dc(this);
// output image size
UINT32 w = 400;
UINT32 h = 300;
// read the source image
UINT32 bw, bh;
HISSRC hSrc = IS40_OpenFileSource(DPIX"bizcover.bmp");
HGLOBAL hImgDIB = IS40_ReadImage(hSrc, &bw, &bh, 2, 0);
IS40_CloseSource(hSrc);
BITMAPINFOHEADER * pInBMIH = (BITMAPINFOHEADER *)hImgDIB;
UINT32 uBitCount;
IS40_DIBBitCount(pInBMIH, &uBitCount);
ASSERT(uBitCount==24);
if (uBitCount!=24) return;
BYTE *pInDIBPix = IS40_DIBPixelStart(pInBMIH);
UINT32 inRowStride = IS40_GetDIBRowStride(bw, 24);
UINT32 srcRowStride = IS40_GetDIBRowStride(w, 24);
UINT dibSize = IS40_GetISDIBSize(w, h, 24, 0);
// resized input
BYTE *pSrcDIB = new BYTE[dibSize];
BITMAPINFOHEADER * pSrcBMIH = (BITMAPINFOHEADER *)pSrcDIB;
pSrcBMIH->biBitCount = 24;
pSrcBMIH->biClrImportant = 0;
pSrcBMIH->biClrUsed = 0;
pSrcBMIH->biCompression = BI_RGB;
pSrcBMIH->biHeight = h;
pSrcBMIH->biPlanes = 1;
pSrcBMIH->biSize = sizeof(BITMAPINFOHEADER);
pSrcBMIH->biSizeImage = dibSize;
pSrcBMIH->biWidth = w;
pSrcBMIH->biXPelsPerMeter = 10000;
pSrcBMIH->biYPelsPerMeter = 10000;
BYTE *pSrcPix = IS40_DIBPixelStart(pSrcBMIH);
// output
BYTE *pOutDIB = new BYTE[dibSize];
BITMAPINFOHEADER * pOutBMIH = (BITMAPINFOHEADER *)pOutDIB;
pOutBMIH->biBitCount = 24;
pOutBMIH->biClrImportant = 0;
pOutBMIH->biClrUsed = 0;
pOutBMIH->biCompression = BI_RGB;
pOutBMIH->biHeight = h;
pOutBMIH->biPlanes = 1;
pOutBMIH->biSize = sizeof(BITMAPINFOHEADER);
pOutBMIH->biSizeImage = dibSize;
pOutBMIH->biWidth = w;
pOutBMIH->biXPelsPerMeter = 0;
pOutBMIH->biYPelsPerMeter = 0;
BYTE *pOutPix = IS40_DIBPixelStart(pOutBMIH);
if (bAVI)
{
AVI_Init();
AVI_FileOpenWrite(&pfile, lpFilename);
AVI_CreateStream(pfile, &ps, frame_rate,
(unsigned long)0,// lpbi->biSizeImage,
(int) pSrcBMIH->biWidth,
(int) pSrcBMIH->biHeight);
AVI_SetOptions(&ps, &psCompressed, pSrcBMIH);
if (!psCompressed) return;
}
// init our Perlin noise source
HISEPERLIN perlin = ISE40_InitializePerlinNoise3D(0);
BYTE *perlinData = new BYTE[w * h];
BYTE *perlinData2 = new BYTE[w * h];
int iFrame = 0;
int z = 0;
double pi = acos(-1);
BYTE *pInStart = pInDIBPix;
IS40_ResizeImage(pInStart, bw, bh, inRowStride,
pSrcPix, w, h, srcRowStride, 3, 0, 0);
memcpy(pOutPix, pSrcPix, h * srcRowStride);
// loop over the total number of frames.
for (z=0;z<totalFrames;z++)
{
ISE40_ClearAllTransforms();
double dz = (double)z;
double totalDone = dz / (totalFrames - 1);
///////////////////////////////////////////////////////////////////
// first effect is some warping, using a
// Perlin noise image as the input to a Bump XForm
int part1Start = 0;
// it will run for the first 1/3 of the animation
int part1End = part1Start + totalFrames / 3;
int part1Size = part1End - part1Start;
if (z >= part1Start && z < part1End)
{
double pz = z - part1Start;
double partDone = pz / (double)(part1Size - 1.);
double fScale = 0.15;
double fZoomScale = part1Size/fScale;
double fZoomSpeed = .5;
double fFeatureSpeed = .01;
double fz = fScale - pz / fZoomScale;
double zpos = 0.001 + pz / 50.;
ISE40_GetPerlinNoise3DSliceBYTE(perlin, perlinData, w, h, w,
fz * fZoomSpeed, zpos * partDone);
// add some high-frequency noise, too
double freq = 2;
for (; freq >= 1. ; freq /= 2)
{
ISE40_GetPerlinNoise3DSliceBYTE(perlin, perlinData2, w, h, w,
freq * fz * fZoomSpeed, zpos * fFeatureSpeed);
ISE40_AddBumpMapDisplacementTransform(w, h,
20. * partDone, perlinData2, w, h);
}
ISE40_AddBumpMapDisplacementTransform(w, h,
50. * partDone, perlinData, w, h);
}
///////////////////////////////////////////////////////////////////
// the next effect, comes in 5 seconds before the end of the first effect
int part2Start = part1End - frame_rate * 5;
int part2End = part2Start + totalFrames / 3; // runs for 1/3 the total time
int part2Size = part2End - part2Start;
// the next two effects use the custom "particle" XForm,
// which is a simple point attractor. but we're using it backwards,
// sortof. instead of attracting, it's pulling image data from the
// outside of the image and shoving it up through the middle,
// turning the image inside out.
partInfo pia;
pia.particles.clear();
particle p0;
if (z >= part2Start && z < part2End)
{
double pz = z - part2Start;
double partDone = pz / (double)(part2Size - 1.);
// vary the strength of the attractor over the lifetime of the effect
double f = 1 + (.5 - cos(partDone * pi) / 2.0);
p0.x = w/2;
p0.y = h/2;
p0.mag = 200 * (1.0 - f);
p0.fK = 1;
p0.maxDist = 400;
p0.mode = particle::linear;
pia.particles.push_back(p0);
ISE40_AddUserTransform(w,h,ParticleXForm, (UINT32)&pia);
}
///////////////////////////////////////////////////////////////////
// the next effect is basically the previous one running in
// reverse - sucking all the image data back in.
int part3Start = part2End;
int part3End = part3Start + totalFrames / 3;
int part3Size = part3End - part3Start;
if (z >= part3Start && z < part3End)
{
double pz = z - part3Start;
double partDone = pz / (double)(part3Size - 1.);
double f = 2. - partDone;
ASSERT(f >= 0);
p0.x = w/2;
p0.y = h/2;
p0.mag = 200 * (1.0 - f);
p0.fK = 1;
p0.maxDist = 400;
p0.mode = particle::linear;
pia.particles.push_back(p0);
ISE40_AddUserTransform(w,h,ParticleXForm, (UINT32)&pia);
}
///////////////////////////////////////////////////////////////////
// this effect comes in part way through the previous one. it's a
// spin XForm, which will vary in direction and intensity over the
// effect's lifetime.
int part4Start = part2End + part3Size / 3;
int part4End = totalFrames - (frame_rate * 4);
int part4Size = part4End - part4Start;
if (z >= part4Start && z < part4End)
{
double pz = z - part4Start;
double partDone = pz / (double)(part4Size - 1.);
double sta = 45.0 * sin(2.0 * pi * partDone);
double rad = 300 * sin(2.0 * pi * partDone);
ISE40_AddSpinTransform(w, h, rad, sta, w/2, h/2);
}
///////////////////////////////////////////////////////////////////
// and finally, the last 4 seconds gets the dissolve. this is a
// "random" XForm which increases in intensity over the for seconds.
int part5Start = totalFrames - (frame_rate * 4);
int part5End = totalFrames - frame_rate / 2;
int part5Size = part5End - part5Start;
if (z >= part5Start && z < part5End)
{
double pz = z - part5Start;
double partDone = pz / (double)(part5Size - 1.);
double sta = w * partDone;
ISE40_AddRandomTransform(w,h,sta);
}
///////////////////////////////////////////////////////////////////
// perform whatever transforms we have lined-up
UINT32 uMode = 2;
ISE40_TransformProcess(pSrcPix, w, h - 20, srcRowStride,
pOutPix, srcRowStride, 3, uMode | (1<<3), RGB(0,0,0));
// draw the image
IS40_StretchDrawDIB(dc.m_hDC, pOutBMIH, 0, 0, w, h);
// add the AVI frame
if (bAVI) AVI_AddFrame(psCompressed, iFrame++, pOutBMIH);
// monitor the progress
char buf[10];
sprintf(buf, "%d\n", z);
OutputDebugString(buf);
}
// add the final frame
if (bAVI) AVI_AddFrame(psCompressed, iFrame++, pSrcBMIH);
// clean up.
ISE40_DestroyPerlinNoise3D(perlin);
GlobalFree(hImgDIB);
// The end...
if (bAVI)
{
AVI_CloseStream(ps, psCompressed, NULL);
AVI_CloseFile(pfile);
AVI_Exit();
}
delete [] pOutDIB;
delete [] pSrcDIB;
delete [] perlinData;
delete [] perlinData2;
}