图像渐变切换动画效果(像素点位移)
[編輯] [转简体] (简体译文)
|
作者:huidong
| 分類:【編程】EasyX
[
23 瀏覽
0 評論
6 贊
7 踩
]
概要
正文
///////////////////////////////////// // // 图像切换动画效果(像素点位移) // // Visual Studio 2022 | EasyX 20220610 // // by huidong <mailhuid@163.com> // 2022.7.16 // #include <easyx.h> #include <stdio.h> #include <time.h> #include <vector> using std::vector; #define SQR(x) ((x)*(x)) #define DISTANCE(x1, y1, x2, y2) sqrt(SQR(x2-x1)+SQR(y2-y1)) struct FloatPoint { double x = 0, y = 0; }; struct MovePoint { FloatPoint pt = {}; // 原位置 double vx = 0, vy = 0; // 毫秒速度 bool isRedundant = false; // 该点是否多余(Src) bool isUsed = false; // 是否已被作为目标点(Dst) }; IMAGE imgSrc, imgDst; vector<MovePoint> vecSrc, vecDst; int nAnimateDuration = 2; // 动画时长(秒) int nFPS = 24; // 帧率 int nDelayMS = (int)((1.0 / nFPS) * 1000); // 每帧的延时(毫秒) void Load() { WCHAR lpszSrcFile[128] = {}; WCHAR lpszDstFile[128] = {}; printf("Source file:"); _getws_s(lpszSrcFile); printf("Destination file:"); _getws_s(lpszDstFile); loadimage(&imgSrc, lpszSrcFile); loadimage(&imgDst, lpszDstFile); } void Record() { IMAGE* pImg[] = { &imgSrc,&imgDst }; vector<MovePoint>* pVec[] = { &vecSrc,&vecDst }; for (int i = 0; i < 2; i++) { SetWorkingImage(pImg[i]); for (int x = 0; x < getwidth(); x++) { for (int y = 0; y < getheight(); y++) { if (getpixel(x, y) == BLACK) { MovePoint pt = { (double)x,(double)y }; pVec[i]->push_back(pt); } } } } } void Calc() { // 平衡源点和目标点数量 size_t sizeSrc = vecSrc.size(); size_t sizeDst = vecDst.size(); if (sizeSrc < sizeDst) { for (size_t i = 0; i < sizeDst - sizeSrc; i++) { vecSrc.push_back(vecSrc[rand() % sizeSrc]); } } else if (sizeSrc > sizeDst) { size_t sizeDiff = sizeSrc - sizeDst; for (size_t i = 0; i < sizeDiff; i++) { vecSrc[sizeSrc / sizeDiff * i].isRedundant = true; } } // 分配目标点,计算速度 size_t sizeSrcNew = vecSrc.size(); for (size_t i = 0; i < sizeSrcNew; i++) { FloatPoint ptBest = { -1,-1 }; double dDistance = -1; int nBestIndex = -1; for (size_t j = 0; j < sizeDst; j++) { // 多余的点选哪个做目标都可以,但是其他点只能选择没用过的 if (vecSrc[i].isRedundant || !vecDst[j].isUsed) { double dThis = DISTANCE( vecSrc[i].pt.x, vecSrc[i].pt.y, vecDst[j].pt.x, vecDst[j].pt.y); if (dThis < dDistance || dDistance == -1) { ptBest = vecDst[j].pt; dDistance = dThis; nBestIndex = j; } } } if (nBestIndex == -1) { printf("\nError.\n"); exit(-1); } if (!vecSrc[i].isRedundant) { vecDst[nBestIndex].isUsed = true; } vecSrc[i].vx = (ptBest.x - vecSrc[i].pt.x) / (double)nAnimateDuration / 1000; vecSrc[i].vy = (ptBest.y - vecSrc[i].pt.y) / (double)nAnimateDuration / 1000; } } // 精确延时函数(可以精确到 1ms,精度 ±1ms) // by yangw80<yw80@qq.com>, 2011-5-4 void HpSleep(int ms) { static clock_t oldclock = clock(); // 静态变量,记录上一次 tick oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick if (clock() > oldclock) // 如果已经超时,无需延时 oldclock = clock(); else while (clock() < oldclock) // 延时 Sleep (1); // 释放 CPU 控制权,降低 CPU 占用率 // Sleep (0); // 更高精度、更高 CPU 占用率 } void Animate(bool reverse = false) { int nDelayFrequency = nAnimateDuration * 1000 / nDelayMS; vector<MovePoint> vecPt = vecSrc; if (reverse) { for (size_t j = 0; j < vecSrc.size(); j++) { vecPt[j].pt.x += vecPt[j].vx * nAnimateDuration * 1000; vecPt[j].pt.y += vecPt[j].vy * nAnimateDuration * 1000; vecPt[j].vx = -vecPt[j].vx; vecPt[j].vy = -vecPt[j].vy; } } for (int i = 0; i < nDelayFrequency; i++) { cleardevice(); for (size_t j = 0; j < vecSrc.size(); j++) { vecPt[j].pt.x += vecPt[j].vx * nDelayMS; vecPt[j].pt.y += vecPt[j].vy * nDelayMS; putpixel((int)vecPt[j].pt.x, (int)vecPt[j].pt.y, BLACK); } FlushBatchDraw(); HpSleep(nDelayMS); } } int main() { Load(); printf("\nLoading..."); Record(); Calc(); initgraph(640, 480); BeginBatchDraw(); setbkcolor(WHITE); settextcolor(BLACK); while (true) { cleardevice(); putimage(0, 0, &imgSrc); outtextxy(0, 0, L"Any key to start."); FlushBatchDraw(); flushmessage(); getmessage(EM_KEY); Animate(); outtextxy(0, 0, L"Any key to replay."); FlushBatchDraw(); flushmessage(); getmessage(EM_KEY); Animate(true); } EndBatchDraw(); closegraph(); return 0; }