CodeBus

分享代码,一起进步~

实现水波纹显示效果

当鼠标滑过时,可以在屏幕上实现漂亮的水波纹效果。 执行效果如下: 源代码如下: /**************************************************** * 程序名称:实现水波纹显示 * * 编译环境:Visual C++ 6.0,EasyX 20130114(beta) * * 作  者:豪 (QQ: 415051674) * * 核心算法:参考网上算法 * * 最后修改:2013/3/20 * ****************************************************/ #include <graphics.h> #include <conio.h> #include <stdio.h> #define PIC_HEIGHT 600 #define PIC_WIDTH 800 void FrameFun(); // 帧逻辑函数,处理每一帧的逻辑 void RenderFun(); // 帧渲染函数,输出每一帧到显示设备 IMAGE src_img; // 原位图 IMAGE dest_img(PIC_WIDTH, PIC_HEIGHT); // 处理后显示的位图 DWORD *img_ptr1; // 原图片片内存指针 DWORD *img_ptr2; // 处理后显示的位图内存指针 // 以下两个 buf 为每一个点的波幅,前者为当前波幅,后者为下一个时刻的波幅。 short *buf = new short[PIC_HEIGHT*PIC_WIDTH+PIC_WIDTH]; short *buf2 = new short[PIC_HEIGHT*PIC_WIDTH+PIC_WIDTH]; void main() { // 初始化设备,加载图片 initgraph(PIC_WIDTH, PIC_HEIGHT); SetWindowText(GetHWnd(), "Wave-水波纹效果(点击产生一个水波纹。移动鼠标连续产生水波纹)"); loadimage(&src_img, "water.jpg"); // 加载图片,大小:800*600 setbkmode(TRANSPARENT); settextcolor(BLACK); setfont(25, 0, "Arial"); // 获得内存指针 img_ptr1 = GetImageBuffer(&src_img); img_ptr2 = GetImageBuffer(&dest_img); // 初始化波幅数组 memset(buf, 0, (PIC_HEIGHT*PIC_WIDTH+PIC_WIDTH) * sizeof(short)); memset(buf2, 0, (PIC_HEIGHT*PIC_WIDTH+PIC_WIDTH) * sizeof(short)); // Let's Go! BeginBatchDraw(); // 双缓冲,闪屏时需要 while(true) { FrameFun(); RenderFun(); FlushBatchDraw(); Sleep(1); } EndBatchDraw(); } // 计算出下一个时刻所有点的波幅 void nextFrame() { for(int i = PIC_WIDTH; i < PIC_HEIGHT*(PIC_WIDTH-1); i++) { // 公式:X0'= (X1+X2+X3+X4) / 2 - X0 buf2[i] = ((buf[i-PIC_WIDTH] + buf[i+PIC_WIDTH] + buf[i-1] + buf[i+1]) >> 1) - buf2[i]; // 波能衰减 buf2[i] -= buf2[i] >> 5; } short *ptmp = buf; buf = buf2; buf2 = ptmp; } // 处理当前时刻波幅影响之后的位图,保存在 dest_img 中 void RenderRipple() { int i = 0; for (int y = 0; y < PIC_HEIGHT; y++) { for (int x = 0; x < PIC_WIDTH; x++) { short data = 1024 - buf[i]; // 偏移 int a = ((x - PIC_WIDTH / 2) * data / 1024) + PIC_WIDTH / 2; int b = ((y - PIC_HEIGHT / 2) * data / 1024) + PIC_HEIGHT / 2; // 边界处理 if (a >= PIC_WIDTH) a = PIC_WIDTH - 1; if (a < 0) a = 0; if (b >= PIC_HEIGHT) b = PIC_HEIGHT - 1; if (b < 0) b = 0; // 处理偏移 img_ptr2[i] = img_ptr1[a + (b * PIC_WIDTH)]; i++; } } } // 鼠标模拟投石头 // 参数说明: // (x, y): 鼠标坐标 // stonesize: “石头”的大小 // stoneweight: 投“石头”的力度 // Ps: 如果产生错误,一般就是数组越界所致,请酌情调整“石头”的大小和“石头”的力度 void disturb(int x, int y, int stonesize, int stoneweight) { // 突破边界不处理 if ((x >= PIC_WIDTH - stonesize) || (x < stonesize) || (y >= PIC_HEIGHT - stonesize) || (y < stonesize)) return; for (int posx=x-stonesize; posx<x+stonesize; posx++) { for (int posy=y-stonesize; posy<y+stonesize; posy++) { if ((posx-x)*(posx-x) + (posy-y)*(posy-y) < stonesize*stonesize) { buf[PIC_WIDTH*posy+posx] += stoneweight; } } } } // 计算fps float getFps() { #define FPS_COUNT 8 static i = 0; static oldTime = GetTickCount(); static float fps; if (i > FPS_COUNT) { i = 0; int newTime = GetTickCount(); int elapsedTime = newTime - oldTime; fps = FPS_COUNT / (elapsedTime / 1000.0f); oldTime = newTime; } i++; return fps; } // 渲染 void RenderFun() { RenderRipple(); putimage(0, 0, &dest_img); char s[5]; sprintf(s, "%.1f", getFps()); outtextxy(0, 0, s); } // 逻辑 void FrameFun() { // 鼠标 if(MouseHit()) { MOUSEMSG msg = GetMouseMsg(); if(msg.uMsg == WM_MOUSEMOVE) { disturb(msg.x, msg.y, 3, 256); } else if(msg.uMsg == WM_LBUTTONDOWN) { disturb(msg.x, msg.y, 3, 2560); } FlushMouseMsgBuffer(); } // 计算下一帧的波幅 nextFrame(); } 完整源代码、图片及编译后的可执行文件请【点击这里下载】。 作者:豪QQ: 415051674

三辆行驶的小车

本程序模拟了以不同速度形式的三辆小车。 源代码如下: //////////////////////////////////////////// // 程序名称:3 辆行驶的小车 // 编译环境:Visual C++ 6.0,EasyX_20120603(beta) // 程序编写:E_SHARE <501748772@qq.com> // 编写时间:2012-6-26 //////////////////////////////////////////// #include <graphics.h> #include <conio.h> #include <stdlib.h> #include <stdio.h> // 声明需要使用的函数 void carstart(int x, int y, int z); void drawbus(); void init(); // 定义全局变量 IMAGE img; //////////////////////////////////////////// void main() { init(); int x=0, y=0, z=0; BeginBatchDraw(); while(!kbhit()) { x += 2; y++; z += 3; if (x > 600) x = -200; if (y > 600) y = -200; if (z > 600) z = -200; carstart(x, y, z); FlushBatchDraw(); Sleep(5); } EndBatchDraw(); closegraph(); } //////////////////////////////////////////// // 初始化函数,初始化窗口大小,获取所画图片 void init() { // 初始化窗口大小 initgraph(600, 600); outtextxy(70, 250, "大家好,新手来报到,希望大家多多指教"); outtextxy(70, 270, "下面你们会看到我程序的效果,程序很简单"); outtextxy(70, 290, "希望以后再跟大家的交流中学到更多,希望自己以后能编出更好的程序"); outtextxy(70, 320, "请按任意键进观看程序执行效果"); // 等待按键按下 getch(); cleardevice(); // 清除上面的文字进入运行效果画面 drawbus(); // 调用绘图函数,绘制 BUS getimage(&img, 80, 40, 180, 90); // 获取 BUS 图片位置,保存在 img 变量中 } ////////////////////////////////////////////////////////////// // 车辆行驶程序,通过 putimge 函数,改变移动的像素来达到图片移动 void carstart(int x, int y, int z) { cleardevice(); putimage(x, 40, &img); setlinestyle(PS_SOLID, NULL, 10); //设置画线的大小 line(0, 135, 600, 135); putimage(y, 220, &img); line(0, 315, 600, 315); putimage(z, 380, &img); line(0, 475, 600, 475); } ////////////////////////////////////////////////////////////// // 绘制 BUS 函数,通过画一些线条,组合它们的位置,组合成一辆小车 void drawbus() { setcolor(RED); setfillstyle(BLUE); fillcircle(120, 120, 10); // 画车的轮胎 fillcircle(200, 120, 10); // 画车的轮胎 line(80, 120, 110, 120); // 画车的底部车板 line(80, 40, 80, 120); // 画车的四周车板 line(130, 120, 190, 120); // 画车的底部车板 line(210, 120, 250, 120); // 画车的底部车板 line(250, 40, 250, 120); // 画车的四周车板 line(80, 40, 250, 40); // 画车的顶部车板 // 画车窗 for(int x = 90, y = 100; x < 190 && y < 190; x += 15, y += 15) { rectangle(x, 60, y, 70); } // 画车门 rectangle(220, 60, 240, 120); line(230, 60, 230, 120); circle(230, 90, 5); } 作者:E_SHAREEmail:501748772@qq.com

图片转化为 ASCII 图,就是很多字符组成图片的那种

本程序实现将图片转换为 ascii 字符的效果。 使用说明 支持 bmp / jpg / gif / emf / wmf / ico 类型的图片。gif 类型的图片仅加载第一帧,不支持透明。我只测试过jpg格式。请把要转换的图片复制到exe程序所在的文件夹,并确保图片名中没有中文等无法输入的字符。运行exe文件即可转换。因为txt文件的行距,字符间距等原因,为了达到最好效果,请先把图片的高度压缩到原来的1/2(宽度不变)转换根据图片的实际大小操作,如果图片过大,请修改图片尺寸,或者手工修改txt中字符的尺寸,以便能够完全显示 备注 这仅仅是一个demo,里边存在不少问题,比如ASCII灰度实际上是按照8*16像素扫描的;实际使用的ASCII灰度值我修改过,并非原来的灰度等等 为了更精确的控制字体的尺寸,间距,行距,html文件是个不错的选择,如果你愿意的话,你可以选择完善这个demo,做成一个很实用的小小的软件,但是让一个东西变得更加精致,并不是我的兴趣所在,所以估计今后我也不会再修改这个程序了。 为了方便你的完善,我给你写了一段灰度扫描的程序,有自己的main(),可以独立的运行。 如果在本程序的基础上修改完善,请保留文件头部的作者信息 联系我 有问题的话,可以给我发邮件:Geodesic <geodesicwl-cpro@yahoo.cn> 因为垃圾邮件的原因,我的邮箱经常的变换,所以请确保你发送的是我最新的地址 更多下载:http://code.google.com/p/c-programming-language/downloads/list 玩的开心o(∩_∩)o 执行效果如图 原图片: 生成的文本效果: 完整源代码 ///////////////////////////////////////////////////// // 程序名称:图片转ASCII点阵demo // 编译环境:Visual C++ 6.0,EasyX 2012-5-1测试版 // 作  者:Geodesic <Geodesicwl-cpro@yahoo.cn> // 最后修改:2012-5-16 ///////////////////////////////////////////////////// //若提示缺少graphics.h(Easyx)头文件,请到这里下载: //www.easyx.cn ///////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> #include <graphics.h> /***********************宏定义***********************/ #define ASC_HEIGHT 8 #define ASC_WIDTH 8 #define ASCII_NUM 32 /*********************结构体定义*********************/ struct ascii { char asc[ASCII_NUM]; int gray[ASCII_NUM]; }as = {' ', '`', '.', '^', ',', ':', '~', '"', '<', '!', 'c', 't', '+', '{', 'i', '7', '?', 'u', '3', '0', 'p', 'w', '4', 'A', '8', 'D', 'X', '%', '#', 'H', 'W', 'M', 0, 5, 7, 9, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 59, 61, 63, 66, 68, 70}; /**********************函数声明***********************/ char SearchAsc(struct ascii as, int gray); void Pic2Asc(struct ascii as, char filename[]); /***********************主函数***********************/ int main(void) { char filename[256]; system("title, 图片转化为ASCII图画demo"); printf("请输入文件名(含扩展名)\n\n"); gets(filename); system("cls"); Pic2Asc(as, filename); printf("转换完成,任意键退出\n\n"); system("pause"); return 0; } /***********************函数体***********************/ //图片转化为ASCII点阵,并保存到txt和html文件中 void Pic2Asc(struct ascii as, char filename[]) { int height, width, gray; IMAGE img; FILE *fpAscPicText; //图像设备初始化 loadimage(&img, filename); SetWorkingImage(&img); height = getheight() / ASC_HEIGHT; width = getwidth() / ASC_WIDTH; if((fpAscPicText = fopen("ASC_PIC.txt", "w")) == NULL) { printf("文件打开失败\n"); exit(0); } //开始转化并保存结果 for(int i = 0; i < height; i++) { for(int j = 0; j < width; j++) { gray = 0; //扫描每一小块的灰度,并计算出平均灰度 for(int h = 0; h < ASC_HEIGHT; h++) for(int w = 0; w < ASC_WIDTH; w++) { gray += GetRValue(RGBtoGRAY(getpixel(j * ASC_WIDTH + w, i * ASC_HEIGHT + h))); } gray /= (ASC_HEIGHT * ASC_WIDTH); gray = (255- gray) * 90 / 255; fputc(SearchAsc(as, gray), fpAscPicText); } fputc('\n', fpAscPicText); } fclose(fpAscPicText); } //二分法查找ASCII字符,就低不就高 char SearchAsc(struct ascii as, int gray) { int lower = 0; int higher = ASCII_NUM; int mid; if(gray <= as.gray[0]) { return as.asc[0]; } else if (gray >= as.gray[ASCII_NUM - 1]) { return as.asc[ASCII_NUM - 1]; } else { while((higher - lower) > 1) { mid = (lower + higher) >> 1; if(gray > as.gray[mid]) { lower = mid; } else { higher = mid; } } return as.asc[lower]; } } 作者:GeodesicEmail:Geodesicwl-cpro@yahoo.cn

力学:鼠标用弹簧挂着一串小方块

看到最近力学程序受欢迎,我就翻出来以前收藏的一个 javascript 脚本,也是力学相关的,模拟出几个小方块用弹簧连接到鼠标上的效果。当鼠标在绘图窗口上移动时,可以看到小方块连续受力的效果。 动态程序就不再抓图了。源代码如下: /////////////////////////////////////////////////// // 程序名称:力学:鼠标用弹簧挂着一串小方块 // 编译环境:Visual C++ 6.0 / 2010,EasyX 20120404(beta) // 原 作 品:很早以前收藏的一个 javascript 脚本,源地址忘了 // 移植作者:yangw80 <yw80@qq.com> // 最后修改:2012-4-27 // #include <graphics.h> #include <math.h> #include <time.h> const int WIDTH = 640; // 窗口宽 const int HEIGHT = 480; // 窗口高 const int BOXCOUNT = 10; // 方块的数量 const int BOXSIZE = 10; // 方块的边长 const double DELTAT = 0.01; // 时间粒度 const double SEGLEN = 10; // 一根弹簧的静止长度 const double SPRINGK = 10; // 弹簧的弹力系数 const double MASS = 1; // 质量 const double GRAVITY = 50; // 重力加速度 const double RESISTANCE = 10; // 空气的阻力系数(阻力=速度*阻力系数) const double STOPVEL = 0.1; // 速度的临界值(低于此值将忽略) const double STOPACC = 0.1; // 重力加速度的临界值(低于此值将忽略) const double BOUNCE = 0.75; // 边框的弹力(反弹速度 = 原速度 * BOUNCE) ////////////////////////////// // 四舍五入 // int round(double x) { return (int)(x < 0 ? x - 0.5 : x + 0.5); } ////////////////////////////// // 浮点坐标 // class FPOINT { public: double X, Y; FPOINT(double x, double y) { X = x; Y = y; } }; ////////////////////////////// // 方块对象 // class BOX { private: double oldx, oldy; public: double X, Y; double dx, dy; // 构造函数 BOX() { oldx = X = dx = 0; oldy = Y = dy = 0; } // 设置方块是否可以见 void setvisible(bool visible) { if (visible) rectangle(round(X), round(Y), round(X) + BOXSIZE - 1, round(Y) + BOXSIZE - 1); } // 绘制方块 void draw() { rectangle(round(oldx), round(oldy), round(oldx) + BOXSIZE - 1, round(oldy) + BOXSIZE - 1); rectangle(round(X), round(Y), round(X) + BOXSIZE - 1, round(Y) + BOXSIZE - 1); oldx = X; oldy = Y; } }; ////////////////////////////// // 方块数组 // BOX g_boxes[BOXCOUNT]; ////////////////////////////// // 计算 g_boxes[i] 和 g_boxes[j] 之间的拉力 // void springForce(int i, int j, FPOINT* spring) { double dx = (g_boxes[i].X - g_boxes[j].X); double dy = (g_boxes[i].Y - g_boxes[j].Y); double len = sqrt(dx * dx + dy * dy); if (len > SEGLEN) { double springF = SPRINGK * (len - SEGLEN); spring->X += (dx / len) * springF; spring->Y += (dy / len) * springF; } } ////////////////////////////// // 动画过程 // void Animate() { // g_boxes[0] 表示鼠标位置,不绘制方块 for (int i = 1 ; i < BOXCOUNT; i++ ) { FPOINT spring(0, 0); // 计算每个方块受前后方块的拉力 springForce(i-1, i, &spring); if (i < (BOXCOUNT - 1)) springForce(i+1, i, &spring); // 空气阻力 FPOINT resist(-g_boxes[i].dx * RESISTANCE, -g_boxes[i].dy * RESISTANCE); // 计算新的加速度 FPOINT accel((spring.X + resist.X) / MASS, (spring.Y + resist.Y) / MASS + GRAVITY); // 计算新的速度 g_boxes[i].dx += (DELTAT * accel.X); g_boxes[i].dy += (DELTAT * accel.Y); // 接近静止时使其不再运动 if (fabs(g_boxes[i].dx) < STOPVEL && fabs(g_boxes[i].dy) < STOPVEL && fabs(accel.X) < STOPACC && fabs(accel.Y) < STOPACC) { g_boxes[i].dx = 0; g_boxes[i].dy = 0; } // 计算移动到的新位置 g_boxes[i].X += g_boxes[i].dx; g_boxes[i].Y += g_boxes[i].dy; // 墙壁的反弹(天花板不反弹) if (g_boxes[i].X > WIDTH - BOXSIZE) { g_boxes[i].X = WIDTH - BOXSIZE; if (g_boxes[i].dx > 0) g_boxes[i].dx = BOUNCE * -g_boxes[i].dx; } if (g_boxes[i].Y > HEIGHT - BOXSIZE) { g_boxes[i].Y = HEIGHT - BOXSIZE; if (g_boxes[i].dy > 0) g_boxes[i].dy = BOUNCE * -g_boxes[i].dy; } if (g_boxes[i].X < 0) { g_boxes[i].X = 0; if (g_boxes[i].dx < 0) g_boxes[i].dx = BOUNCE * -g_boxes[i].dx; } // 在新位置画出方块 g_boxes[i].draw(); } } ////////////////////////////// // 延时器 // void MySleep(int t) { static clock_t oldclock = clock(); // 静态变量,记录上一次 tick t *= CLOCKS_PER_SEC / 1000; // 将毫秒转换为 tick oldclock += t; // 更新 tick while(clock() < oldclock) // 延时 Sleep(1); // 释放 CPU 控制权,降低 CPU 占用率 } ////////////////////////////// // 主函数 // void main() { // 初始化绘图环境 initgraph(WIDTH, HEIGHT); // 设置绘图窗口宽高 setcolor(GREEN); // 设置绘图颜色 setwritemode(R2_XORPEN); // 设置异或绘图模式 BeginBatchDraw(); // 开始批量绘图模式 // 初始化所有方块 for (int i = 0; i < BOXCOUNT; i++) { g_boxes[i].setvisible(i != 0); // 除了第 0 个方块,都设置为可见 g_boxes[i].draw(); } // 进入主循环 MOUSEMSG m; // 保存鼠标消息的变量 while(true) { // 处理鼠标消息 while (MouseHit()) { m = GetMouseMsg(); // 获取一条鼠标消息 switch(m.uMsg) { case WM_MOUSEMOVE: g_boxes[0].X = m.x; // g_boxes[0] 表示鼠标位置(不绘制) g_boxes[0].Y = m.y; break; case WM_RBUTTONUP: // 按鼠标右键退出程序 return; } } // 绘制一帧动画并显示 Animate(); FlushBatchDraw(); // 延时 20 毫秒 MySleep(20); } }

力学:流体(Liquid)(VC版本)

这个程序原本是测试 HTML 5 效果的一个演示程序,动态效果很是漂亮。现在,这个精彩的程序经 krissi 移植到了 vc 上。 以下是完整源代码: /////////////////////////////////////////////////// // 程序名称:流体(Liquid) // 编译环境:Visual C++ 6.0 / 2010,EasyX 20120404(beta) // 原 作 品:http://spielzeugz.de/html5/liquid-particles.html (HTML5) // 移植作者:krissi <zh@easyx.cn> // 最后修改:2012-4-5 // #include <graphics.h> #include <math.h> #include <time.h> #define WIDTH 1024 // 屏幕宽 #define HEIGHT 576 // 屏幕高 #define NUM_MOVERS 600 // 小球数量 #define FRICTION 0.96f // 摩擦力 // 定义小球结构 struct Mover { COLORREF color; // 颜色 float x, y; // 坐标 float vX, vY; // 速度 }; // 定义全局变量 Mover movers[NUM_MOVERS]; // 小球数组 int mouseX, mouseY; // 当前鼠标坐标 int mouseVX, mouseVY; // 鼠标速度 int prevMouseX, prevMouseY; // 上次鼠标坐标 bool isMouseDown; // 鼠标左键是否按下 DWORD* pBuffer; // 显存指针 // 初始化 void init() { // 初始化小球数组 for (int i = 0; i < NUM_MOVERS; i++) { movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); movers[i].x = WIDTH * 0.5; movers[i].y = HEIGHT * 0.5; movers[i].vX = float(cos(float(i))) * (rand() % 34); movers[i].vY = float(sin(float(i))) * (rand() % 34); } // 初始化鼠标变量 mouseX = prevMouseX = WIDTH / 2; mouseY = prevMouseY = HEIGHT / 2; // 设置随机种子 srand((unsigned int)time(NULL)); // 获取显存指针 pBuffer = GetImageBuffer(NULL); } // 全屏变暗 50% void darken() { for(int i = WIDTH * HEIGHT - 1; i >= 0; i--) if (pBuffer[i] != 0) pBuffer[i] = RGB(GetRValue(pBuffer[i]) >> 1, GetGValue(pBuffer[i]) >> 1, GetBValue(pBuffer[i]) >> 1); } // 绝对延时 void delay(DWORD ms) { static DWORD oldtime = GetTickCount(); while(GetTickCount() - oldtime < ms) Sleep(1); oldtime = GetTickCount(); } // 绘制动画(一帧) void run() { darken(); // 全屏变暗 mouseVX = mouseX - prevMouseX; mouseVY = mouseY - prevMouseY; prevMouseX = mouseX; prevMouseY = mouseY; float toDist = WIDTH * 0.86f; float stirDist = WIDTH * 0.125f; float blowDist = WIDTH * 0.5f; for(int i = 0; i < NUM_MOVERS; i++) { float x = movers[i].x; float y = movers[i].y; float vX = movers[i].vX; float vY = movers[i].vY; float dX = x - mouseX; float dY = y - mouseY; float d = (float)sqrt(dX * dX + dY * dY); dX = d ? dX / d : 0; dY = d ? dY / d : 0; if (isMouseDown && d < blowDist) { float blowAcc = (1 - (d / blowDist)) * 14; vX += dX * blowAcc + 0.5f - float(rand()) / RAND_MAX; vY += dY * blowAcc + 0.5f - float(rand()) / RAND_MAX; } if (d < toDist) { float toAcc = (1 - (d / toDist)) * WIDTH * 0.0014f; vX -= dX * toAcc; vY -= dY * toAcc; } if (d < stirDist) { float mAcc = (1 - (d / stirDist)) * WIDTH * 0.00026f; vX += mouseVX * mAcc; vY += mouseVY * mAcc; } vX *= FRICTION; vY *= FRICTION; float avgVX = (float)fabs(vX); float avgVY = (float)fabs(vY); float avgV = (avgVX + avgVY) * 0.5f; if (avgVX < 0.1) vX *= float(rand()) / RAND_MAX * 3; if (avgVY < 0.1) vY *= float(rand()) / RAND_MAX * 3; float sc = avgV * 0.45f; sc = max(min(sc, 3.5f), 0.4f); float nextX = x + vX; float nextY = y + vY; if (nextX > WIDTH) { nextX = WIDTH; vX *= -1; } else if (nextX < 0) { nextX = 0; vX *= -1; } if (nextY > HEIGHT){ nextY = HEIGHT; vY *= -1; } else if (nextY < 0) { nextY = 0; vY *= -1; } movers[i].vX = vX; movers[i].vY = vY; movers[i].x = nextX; movers[i].y = nextY; // 画小球 setcolor(movers[i].color); setfillstyle(movers[i].color); fillcircle(int(nextX + 0.5), int(nextY + 0.5), int(sc + 0.5)); } } // 主函数 void main() { // 创建绘图窗口 initgraph(WIDTH, HEIGHT); // 启用批绘图模式 BeginBatchDraw(); // 初始化 init(); // 鼠标消息变量 MOUSEMSG m; while(true) { // 处理鼠标消息 while(MouseHit()) { m = GetMouseMsg(); switch(m.uMsg) { case WM_MOUSEMOVE: mouseX = m.x; mouseY = m.y; break; case WM_LBUTTONDOWN: isMouseDown = true; break; case WM_LBUTTONUP: isMouseDown = false; break; } } // 绘制动画 run(); // 显示缓存的绘制内容 FlushBatchDraw(); // 延时 20 毫秒 delay(20); } // 关闭 EndBatchDraw(); closegraph(); }

力学:弹跳球模拟程序

本程序根据物理原理模拟一个球自由落体并持续弹跳的效果,小球只受重力影响,忽略空气阻力,反弹时能量损耗 10%。 程序源代码如下: //////////////////////////////////////////////////////////////////// // 程序名称:物理反弹球模拟程序 // 编译环境:VC6.0 / 2010,EasyX 2011惊蛰版 // 作  者:yw80@qq.com // 最后修改:2012-3-29 // #include <graphics.h> #include <conio.h> void main() { double h = 300; // 高度 double v = 0; // 速度(方向向下) double dv = 9.8 / 50; // 加速度(每 1/50 秒) // 初始化绘图窗口 initgraph(640, 480); // 画地平线 line(100, 421, 540, 421); while(!_kbhit()) { v += dv; // 根据加速度计算速度 h -= (v - dv / 2); // 计算高度 // 如果高度低于地平线,实现反弹,速度方向取反 if (h <= 0) { h += (v - dv / 2); v = - v * 0.9; // 反弹时能量损耗 10% } // 画绿色球 setcolor(GREEN); circle(320, 400 - int(h), 20); Sleep(20); // 延时(每帧延时 1/50 秒) // 擦掉球 setcolor(BLACK); circle(320, 400 - int(h), 20); } // 关闭绘图窗口 closegraph(); }

纪念披头士摇滚乐队(Beatles)的程序(VC版本)

西班牙程序员 Roman Cortes 用纯 javascript 脚本编写了一个纪念披头士摇滚乐队(Beatles)的程序,动态效果很是漂亮。现在,这个精彩的程序经 krissi 移植到了 vc 上。以下是执行效果抓图(炫丽的动态效果还是需要您亲自编译才能看到): 由歌词渐变为头像,再变为歌词,这个精彩的转换过程需要读者亲自去体验。 完整的 VC 源代码如下: //////////////////////////////////////////////////////////////////// // 程序名称:纪念披头士摇滚乐队(Beatles) // 编译环境:VC6.0 / 2010,EasyX 2011惊蛰版 // 原 作 者:西班牙程序员 Roman Cortes // 原 程 序:http://www.romancortes.com/blog/a-tribute-to-the-beatles/ (javascript 版本) // 移植作者:krissi <zh@easyx.cn> // 最后修改:2012-3-5 // #include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.1415926535 #define WIDTH 640 #define HEIGHT 480 // 定义字符元素结构体 struct ELEMENT { TCHAR c; int x, y; int xo, yo; int xd, yd; }; // 定义全局变量 TCHAR g_beatles[] // 歌词 = _T("Yesterday,\nAll my troubles seemed so far away,\nNow it looks as though they're here to stay,\nOh, I believe in yesterday.\nSuddenly,\nI'm not half the man I used to be,\nThere's a shadow hanging over me,\nOh, yesterday came suddenly.\nWhy she\nHad to go I don't know, she wouldn't say.\nI said,\nSomething wrong, now I long for yesterday.\nYesterday,\nLove was such an easy game to play,\nNow I need a place to hide away,\nOh, I believe in yesterday.\nWhy she\nHad to go I don't know, she wouldn't say.\nI said,\nSomething wrong, now I long for yesterday.\nYesterday,\nLove was such an easy game to play,\nNow I need a place to hide away,\nOh, I believe in yesterday."); char g_data[] // 字符坐标数据 = "Q;PQD0R1F1CMF=;I73F5=QP2<OQUN0=@49CA4KK54BOQM?BW;46G1CETWSVGB;QC0EOISUKVRF3EHL44C2T?RESL17KP:GA6@@MM6P@L59I1MA;ME?O58MKLT3CSNS6PD<:KRSCORFU8WLAURN=P:03CFP3Q>WUHCWNAOP:8LF77E43BL4S?UEHNW?27UGBFJ2CC0WNI9;G?P1;G0GL35WRTMSBVB:@LJI>I03=J:RF1>>V3U?M3G=U7BF;GQ=E4=8RFBU23IMQ>GH1PINK:7D:JWC0UEPM9L>MR<SS7BL3B2LOSGW1;UI4MN:U>A6QRF?;>C7TLCU89CP?92A2U7UQCOEK<9A1S9W@7>AL562GTI7OF9?IE2UQPNSVNFK=PML1LNKFB8A32T4QOJ9W=QK?CEP6VA;UMQTA5K35W64MO25V3E5G7QC3BC=S0SB=A2GAO7VBTF8JF?BKLGQ8WPD>RECRPWSP9BPVKA2O56N?Q7DOO=3RDW;W53;QORWIF1:L22KV5SKV4=;@3=6W@0ECMJTBODP3HL=8TUMD2N1CNFSFR71D=PE>O?IN3EA5IM6HPSP@>U2S@OBG81NR551533394<K5TMTD076=;@N<C9MV>VATGMVBCR>TSKU5NGIQVES?;UD:URR7T?1E8GEAIUGS0O>PVS1GSWTP@SLLKKC?S3@34EUK<9HQ9J90:VGGI<Q6JIAG37UF32874DW3WC7>C82GGMI=?UOOOPL99:8H;>IBMLC?N3:W>4RRTR6MVA0NNEHSIFN6S?CTHGBA8A25Q;=R5@E6IT=;MADSN55909<UL>94MOWR4TIU59NGM?L4AJVN4LJG@1RRLO0CR>33<94>KE6Q5AI8V==9;T5CNU2F;O0O3GEKNISQQER0?U8@X8O1RUM7U@M8N9TQ=>UL=TQ=<ICG;JF6K5GH3<U4874M6D2QGP=PMQ>G26GESGGFC=GF3POOMNONJ3QCGT<WTRI6766;7F>H4=0ULE6;A@DRNK17D1RTD4FVTPO5WA6DVK1CVI7Q@Q?PPCQCD>QEH;FQVQ9OBQLHI?4R9P>QM3KJSDPN<N7FME6I:5T;7TE5889<RM02VMCIISGMG;0AQD:8A@MDFOCQMOC;G@;OWKK5CN66Q=GE=BTDTNIIR;M16QE3OOEW>2M87>RTL@2P:=JU6RPP@5T?>GMGASRQSQVVGS6>2=GB?RQ=JJ7AHFV@FD:NNVDPGE?<CTVCHIGP=IGKQM77H<HQ@A?2<U85FWOEN3I<1S3N1BI5P1HIQEHDHJ3QVNPMIMG@S:GVADRN5W<@JEDDHD3<KJUO28UQW>T4@J15PLURMQSER><G8GA;WGW<H75;40222J1L1W57EJ7QO5WD4D16:85DHNQN7:LFMD=>UB=K>PP3S8HJ7HVPP>;SANMTHVO0O0HBOP5M1ABQ73GE@O5@6P6PU9WRHQQ3W;F6A@ECVOWI1S5E35@<?M728A<<EPB:H:TODGAEJ41IUCUNTATBFU5TGE6=762>=P2KS97O89U2:U>OLB91J>FF>2MWV8P;1F<R1"; int an = -1; // 动画“值” int dir = -1; // 动画运行方向 bool bAni = false; // 动画运行标志 ELEMENT g_Element[520]; // 字符元素数组。共 520 个字符。 // 初始化每个字符元素 void init() { TCHAR s[45]; // 最长的一句歌词有 44 个字符 int x, y; // 每个字符的坐标 int rowwidth; // 每句歌词的宽度 int ig = 0, ir = 0, ie = 0; // 下标变量(全部歌词下标、一句歌词下标、字符元素下标) y = (HEIGHT - 470) / 2; // 计算第一行的 y 坐标 // 计算每个字符元素的起始坐标 while(ie < 520) { if ((g_beatles[ig] != '\n') && (g_beatles[ig] != 0)) { s[ir++] = g_beatles[ig]; } else { s[ir] = 0; rowwidth = textwidth(s); x = (WIDTH - rowwidth) / 2; for(int i = 0; i < ir; i++) { if (s[i] != ' ') { g_Element[ie].c = s[i]; g_Element[ie].x = g_Element[ie].xo = x; g_Element[ie].y = g_Element[ie].yo = y; outtextxy(g_Element[ie].xo, y, s[i]); x += textwidth(g_Element[ie].c); ie++; } else x += textwidth(' '); } ir = 0; y += 19; } ig++; } // 解码每个字符元素的终止坐标 for (int n = 0; n < 520; n++) { int p = g_data[n * 3] * 1600 + g_data[n * 3 + 1] * 40 + g_data[n * 3 + 2] - 78768; g_Element[n].yd = p % 284; g_Element[n].xd = (p - g_Element[n].yd) / 284; g_Element[n].yd += 74 + (HEIGHT - 470) / 2; g_Element[n].xd += 111 + (WIDTH - 450) / 2; } // 显示刷新 FlushBatchDraw(); } // 动画 void ani() { int i, m, n; // 计算需要运动的字符元素 for (n = 0; n < 130; n++) { m = 519 - n; if ((an - n >= 0) && (an - n <= 30)) { double b = (cos((an - n) * PI / 30) + 1) / 2; double a = 1 - b; for (i = 0; i <= 130; i += 130) { g_Element[n + i].x = int((g_Element[n + i].xd) * a + g_Element[n + i].xo * b + 0.5); g_Element[n + i].y = int((g_Element[n + i].yd) * a + g_Element[n + i].yo * b + 0.5); g_Element[m - i].x = int((g_Element[m - i].xd) * a + g_Element[m - i].xo * b + 0.5); g_Element[m - i].y = int((g_Element[m - i].yd) * a + g_Element[m - i].yo * b + 0.5); } } } // 显示全部字符元素 for (i = 0; i < 520; i++) outtextxy(g_Element[i].x, g_Element[i].y, g_Element[i].c); an += dir; if ((an < 0) || (an > 160)) bAni = false; } // 动画事件 void anim() { dir *= -1; // 改变动画方向 if ((an < 0) || (an > 160)) bAni = true; } // 主函数 void main() { // 环境初始化 initgraph(640, 480); // 创建绘图窗口 setbkcolor(WHITE); // 设置背景色为白色 setcolor(RGB(64, 64, 64)); // 设置文字颜色为深灰色 setbkmode(TRANSPARENT); // 设置文字背景为透明 cleardevice(); // 清除屏幕 BeginBatchDraw(); // 设置批量绘图模式 setfont(-15, 0, _T("Arial"), 0, 0, FW_BOLD, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH); // 设置字体 // 初始化每个字符元素 init(); // 动画过程 char c = 0; while(c != 27) { if (_kbhit()) { c = _getch(); anim(); } if (bAni) { cleardevice(); // 清屏 ani(); // 执行动画 FlushBatchDraw(); // 显示刷新 } Sleep(30); } // 结束并清理资源 EndBatchDraw(); closegraph(); }

情人节的红玫瑰

2012年2月14日情人节就要来临了,西班牙程序员 Roman Cortes 用纯 javascript 脚本编写了红色玫瑰花。 现在,这个精彩的程序经 krissi 移植到了 vc 上。以下是执行效果: 这朵漂亮的玫瑰花,每一个点都是用数学算法计算出来的,没有用任何图片贴图。 完整的 vc 源代码如下: //////////////////////////////////////////////////////////////////// // 程序名称:情人节的玫瑰 // 编译环境:VC6.0 / VC2010,EasyX 2011惊蛰版 // 原 作 者:西班牙程序员 Roman Cortes // 原 程 序:http://js1k.com/2012-love/demo/1022 (javascript 版本) // 移植作者:krissi <zh@easyx.cn> // 最后修改:2012-2-13 // 注:程序中的很多精简变量名都沿用原 javascript 中的变量名。 // #include <graphics.h> #include <conio.h> #include <math.h> // 定义全局变量 int rosesize = 500; int h = -250; // 定义结构体 struct DOT { double x; double y; double z; double r; // 红色 double g; // 绿色 // b(蓝色) 通过 r 计算 }; // 计算点 bool calc(double a, double b, double c, DOT &d) { double j, n, o, w, z; if(c > 60) // 花柄 { d.x = sin(a * 7) * (13 + 5 / (0.2 + pow(b * 4, 4))) - sin(b) * 50; d.y = b * rosesize + 50; d.z = 625 + cos(a * 7) * (13 + 5 / (0.2 + pow(b * 4, 4))) + b * 400; d.r = a * 1 - b / 2; d.g = a; return true; } double A = a * 2 - 1; double B = b * 2 - 1; if(A * A + B * B < 1) { if(c > 37) // 叶 { j = (int(c) & 1); n = j ? 6 : 4; o = 0.5 / (a + 0.01) + cos(b * 125) * 3 - a * 300; w = b * h; d.x = o * cos(n) + w * sin(n) + j * 610 - 390; d.y = o * sin(n) - w * cos(n) + 550 - j * 350; d.z = 1180 + cos(B + A) * 99 - j * 300; d.r = 0.4 - a * 0.1 + pow(1 - B * B, -h * 6) * 0.15 - a * b * 0.4 + cos(a + b) / 5 + pow(cos((o * (a + 1) + (B > 0 ? w : -w)) / 25), 30) * 0.1 * (1 - B * B); d.g = o / 1000 + 0.7 - o * w * 0.000003; return true; } if(c > 32) // 花萼 { c = c * 1.16 - 0.15; o = a * 45 - 20; w = b * b * h; z = o * sin(c) + w * cos(c) + 620; d.x = o * cos(c) - w * sin(c); d.y = 28 + cos(B * 0.5) * 99 - b * b * b * 60 - z / 2 - h; d.z = z; d.r = (b * b * 0.3 + pow((1 - (A * A)), 7) * 0.15 + 0.3) * b; d.g = b * 0.7; return true; } // 花 o = A * (2 - b) * (80 - c * 2); w = 99 - cos(A) * 120 - cos(b) * (-h - c * 4.9) + cos(pow(1 - b, 7)) * 50 + c * 2; z = o * sin(c) + w * cos(c) + 700; d.x = o * cos(c) - w * sin(c); d.y = B * 99 - cos(pow(b, 7)) * 50 - c / 3 - z / 1.35 + 450; d.z = z; d.r = (1 - b / 1.2) * 0.9 + a * 0.1; d.g = pow((1 - b), 20) / 4 + 0.05; return true; } return false; } // 主函数 void main() { // 定义变量 short *zBuffer; int x, y, z, zBufferIndex; DOT dot; // 初始化 initgraph(640, 480); // 创建绘图窗口 setbkcolor(WHITE); // 设置背景色为白色 cleardevice(); // 清屏 // 初始化 z-buffer zBuffer = new short[rosesize * rosesize]; memset(zBuffer, 0, sizeof(short) * rosesize * rosesize); for(int j = 0; j < 2000 && !_kbhit(); j++) // 按任意键退出 { for(int i = 0; i < 10000; i++) // 减少是否有按键的判断 if(calc(double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, rand() % 46 / 0.74, dot)) { z = int(dot.z + 0.5); x = int(dot.x * rosesize / z - h + 0.5); y = int(dot.y * rosesize / z - h + 0.5); if (y >= rosesize) continue; zBufferIndex = y * rosesize + x; if(!zBuffer[zBufferIndex] || zBuffer[zBufferIndex] > z) { zBuffer[zBufferIndex] = z; // 画点 int r = ~int((dot.r * h)); if (r < 0) r = 0; if (r > 255) r = 255; int g = ~int((dot.g * h)); if (g < 0) g = 0; if (g > 255) g = 255; int b = ~int((dot.r * dot.r * -80)); if (b < 0) b = 0; if (b > 255) b = 255; putpixel(x + 50, y - 20, RGB(r, g, b)); } } Sleep(1); } // 退出 delete []zBuffer; _getch(); closegraph(); }

火焰效果模拟程序

模拟火焰效果的程序。 执行效果抓图如下: 通过修改色系,本程序可以实现红色火焰、蓝色火焰、绿色火焰三种效果,在 InitFire() 函数中取消相应行注释可以实现不同色系的火焰效果。 完整的源代码如下: /////////////////////////////////////////////////// // 程序名称:火焰模拟程序 // 编译环境:VC6.0 / VC2010,EasyX 2011惊蛰版 // 作  者:yangw80 <yw80@qq.com> // 最后修改:2012-1-21 // PS: 核心算法参考的微软范例,在此感谢。 // #include <graphics.h> #include <conio.h> #include <time.h> // 宏常量 #define WIDTH 640 #define HEIGHT 480 #define FIREWIDTH 320 #define FIREHEIGHT 180 // 全局变量 COLORREF g_Colors[193]; // 火焰色系中使用的全部颜色 BYTE g_Fire[FIREWIDTH]; // 火焰数据 BYTE g_Bits[FIREHEIGHT * FIREWIDTH]; // 火焰数据 // 火焰属性 int m_nDecay = 5; // 衰减度,范围 [1, 100],默认 5 int m_nFlammability = 385; // 易燃性,范围 [1, 399],默认 385 int m_nMaxHeat = 192; // 最高热度,范围 [0, 192],默认 192 int m_nSpreadRate = 20; // 传播速率,范围 [1, 100],默认 20 int m_nSize = 160; // 火源宽度,范围 [40, FIREWIDTH],默认 160 int m_nSmoothness = 1; // 平滑度,范围 [0, 5],默认 1 int m_nDistribution = 1; // 分布,范围 [0, 10],默认 1 int m_nChaos = 50; // 混沌,范围 [1, 100],默认 50 // 初始化火焰 void InitFire() { // 初始化颜色 int r, g, b; // 默认红色火焰。根据注释选择不同的火焰效果 b = 256+256+255; g = 256+255; r = 255; // 红色火焰 // r = 256+256+255; g = 256+255; b = 255; // 蓝色火焰 // g = 256+256+255; b = 256+255; r = 255; // 绿色火焰 // 生成火焰色系 for(int i = 192; i >= 0; i--) { g_Colors[i] = RGB((r > 255) ? 255 : r, (g > 255) ? 255 : g, (b > 255) ? 255 : b); r = (r > 3) ? (r - 4) : 0; g = (g > 3) ? (g - 4) : 0; b = (b > 3) ? (b - 4) : 0; } // 置空火焰数组 memset(g_Fire, 0, FIREWIDTH); memset(g_Bits, 0, FIREWIDTH * FIREHEIGHT); } // 画色系 void DrawColorScheme() { POINT s[8] = { {0, 450}, {580, 450}, {580, 420}, {610, 420}, {610, 0}, {639, 0}, {639, 479}, {0, 479} }; HRGN rgn1 = CreatePolygonRgn(s, 8, WINDING); HRGN rgn2 = CreateEllipticRgn(550, 390, 611, 451); CombineRgn(rgn1, rgn1, rgn2, RGN_DIFF); // 定义裁剪区域 setcliprgn(rgn1); // 设置区域 rgn 为裁剪区 DeleteObject(rgn1); DeleteObject(rgn2); // 画色系 int c, x, y; for (int i = 0; i < 1120; i++) { c = int(i / 5.8); x = (i <= 479 ? 639 : 639 - i + 479); y = (i <= 479 ? i : 479); setcolor(BGR(g_Colors[c])); line(0, 0, x, y); } // 取消裁剪区域 setcliprgn(NULL); } // 计算火焰的每个点 inline void BurnPoint(BYTE* pRow, BYTE* pNextRow) { BYTE* pTarget; int off = rand() % (m_nDistribution + 1); int val = m_nDecay + 1; val = rand() % val; val = *pNextRow - val; pTarget = (rand() & 1) ? pRow + off : pRow - off; *pTarget = (val > 0) ? (BYTE)val : 0; } // 计算火焰 void RenderFlame() { int xStart, xEnd, x, y; BYTE* pRow; BYTE* pNextRow; xStart = (FIREWIDTH - m_nSize) / 2; xEnd = xStart + m_nSize + 1; pRow = g_Bits; for (x = 0; x < FIREWIDTH; x++) { if (x < (xStart + m_nDistribution) || x >= (xEnd - m_nDistribution)) g_Fire[x] = 0; *pRow++ = g_Fire[x]; } for (y = FIREHEIGHT - 1; y > 0; y--) // y = 0 火焰最大;y++ 火焰变小 { pRow = (g_Bits + FIREWIDTH * y); pNextRow = (g_Bits + FIREWIDTH * (y - 1)); if (rand() & 1) { for (x = 0; x < FIREWIDTH; x++) { BurnPoint(pRow, pNextRow); pRow++; pNextRow++; } } else { pRow += FIREWIDTH - 1; pNextRow += FIREWIDTH - 1; for (x = 0; x < FIREWIDTH; x++) { BurnPoint(pRow, pNextRow); pRow--; pNextRow--; } } } if (rand() % (400 - m_nFlammability) == 0) { int off = m_nSize - 5; off = rand() % off; off += xStart; for (x = off; x < off + 5; x++) g_Fire[x] = 239; } for (x = xStart; x < xEnd; x++) { if (g_Fire[x] < m_nMaxHeat) { int val = rand() % m_nChaos + 1; val -= m_nChaos / 2; val += m_nSpreadRate; val += g_Fire[x]; if ( val > m_nMaxHeat) g_Fire[x] = m_nMaxHeat; else if (val < 0) g_Fire[x] = 0; else g_Fire[x] = val; } else g_Fire[x] = m_nMaxHeat; } if (m_nSmoothness > 0) { xStart += m_nSmoothness; xEnd -= m_nSmoothness; for (x = xStart; x < xEnd; x++) { int val = 0; for (y = x - m_nSmoothness; y < x + 1 + m_nSmoothness; y++) val += g_Fire[y]; g_Fire[x] = val / (2 * m_nSmoothness + 1); } } } // 显示火焰 void PaintFlame(int offset_x, int offset_y) { DWORD* pDst = GetImageBuffer(); // 获取显存指针 for(int y = 0; y < FIREHEIGHT; y++) for(int x = 0; x < FIREWIDTH; x++) { COLORREF c = g_Colors[g_Bits[y * FIREWIDTH + x]]; pDst[(offset_y - y) * WIDTH + offset_x + x] = c; } FlushBatchDraw(); // 使显存操作生效 } // 主函数 void main() { initgraph(WIDTH, HEIGHT); // 创建绘图窗口 srand((unsigned)time(NULL)); // 设置随机种子 InitFire(); // 初始化火焰 DrawColorScheme(); // 绘制色系图 while(!kbhit()) // 按任意键退出 { RenderFlame(); // 渲染火焰 PaintFlame(145, 320); // 显示火焰 Sleep(33); // 延时 } closegraph(); }

调色板动画程序(以 EasyX 平台举例)

在 Turbo C 时代的图形编程有“调色板”的概念,但在真彩色时代,调色板的概念已经不完全相同了。 EasyX 模拟了 Turbo C 的大部分功能。由于上述原因,调色板并没有实现。如果要使用调色板,可以通过 Windows GDI 函数实现。 以下范例程序演示了这一过程: (由于是动画程序,贴图无法展现效果,因此省略了贴图) /////////////////////////////////////////////////// // 程序名称:基于 EasyX 的调色板动画程序 // 编译环境:VC6.0 / VC2010,EasyX 2011惊蛰版 // 作  者:krissi <zh@easyx.cn> // 最后修改:2012-1-4 // #include <graphics.h> #include <conio.h> #include <math.h> #define WIDTH 640 #define HEIGHT 480 #define PI 3.1415926535 HPALETTE g_hPal, g_hOldPal; // 调色板句柄 LOGPALETTE* g_pLOGPAL; // 调色板信息结构体 HDC g_hDC; // EasyX 窗口的 DC 句柄 // 初始化 void Init() { initgraph(WIDTH, HEIGHT); // 创建绘图窗口 g_hDC = GetImageHDC(); // 获取绘图窗口的句柄 // 设定动画色系 int r = 216; int g = 0; int b = 0; /////////////////// // 初始化调色板 // 为调色板结构体分配内存(该调色板含有 32 种颜色) // 比实际多四个字节是增加了 palVersion 和 palNumEntries 两个成员, // 详见 MSDN 中 LOGPALETTE 的定义 g_pLOGPAL = (LOGPALETTE*)new PALETTEENTRY[33]; g_pLOGPAL->palVersion = 0x300; g_pLOGPAL->palNumEntries = 32; for(int i = 0; i < 32; i++) { g_pLOGPAL->palPalEntry[i].peRed = BYTE(r * sin(PI * i / 32)); g_pLOGPAL->palPalEntry[i].peGreen = BYTE(g * sin(PI * i / 32)); g_pLOGPAL->palPalEntry[i].peBlue = BYTE(b * sin(PI * i / 32)); g_pLOGPAL->palPalEntry[i].peFlags = PC_RESERVED; } g_hPal = CreatePalette(g_pLOGPAL); // 创建调色板 g_hOldPal = SelectPalette(g_hDC, g_hPal, false); // 选入调色板 } // 改变调色板 void ChangePal() { // 使调色板的颜色循环更换 PALETTEENTRY t = g_pLOGPAL->palPalEntry[0]; for(int i = 0; i < 31; i++) g_pLOGPAL->palPalEntry[i] = g_pLOGPAL->palPalEntry[i + 1]; g_pLOGPAL->palPalEntry[31] = t; // 使调色板的修改生效 AnimatePalette(g_hPal, 0, 32, g_pLOGPAL->palPalEntry); RealizePalette(g_hDC); } // 画图案 void Draw() { HBRUSH hBrush; int x1, y1; COLORREF c; for (int y = 0; y < 64; y++) for (int x = 0; x < 64; x++) { RECT rt = { x * WIDTH / 64 + 1, y * HEIGHT/ 64 + 1, (x + 1) * WIDTH / 64, (y + 1) * HEIGHT/ 64 }; x1 = x > 31 ? 63 - x : x; y1 = y > 31 ? 63 - y : y; c = PALETTEINDEX(min(x1, y1)); hBrush = CreateSolidBrush(c); FillRect(g_hDC, &rt, hBrush); DeleteObject(hBrush); } } // 退出 void Quit() { SelectPalette(g_hDC, g_hOldPal, false); DeleteObject(g_hPal); delete [] g_pLOGPAL; closegraph(); } // 主函数 void main() { Init(); // 初始化 while(!_kbhit()) // 按任意键退出 { ChangePal(); // 改变调色板 Draw(); // 画图案(每次画的都一样,动态图像是因为调色板的改变) FlushBatchDraw(); // 使 GDI 绘图函数生效 Sleep(33); // 延时 } Quit(); // 退出 }