RotateImage_Alpha 透明图像旋转函数(原创)
[編輯] [转简体] (简体译文)
|
作者:huidong
| 分類:【編程】EasyX
[
29 瀏覽
0 評論
6 贊
8 踩
]
概要
正文
旋转图像最关键的就是坐标旋转公式,其实这个东西很简单,可以了解:http://huidong.xyz/index.php?mode=2&id=406
之前我也写过类似的东西,不过是单个点的,见 http://huidong.xyz/?mode=2&id=365
那个里面有一些小错误,不过用在那个需求背景下是没有问题的。
这回我本来是不打算自己来写这个旋转函数的,但是无奈 EasyX 的 rotateimage 不支持含有透明信息的图像的旋转,要是旋转了会失真,导致奇妙的色彩效果。而 Codebus 上面有位作者(Lost)写了个支持透明图像的旋转函数,但是据我测试有很多 bug(见 https://codebus.cn/lostperson/rotate ),所以没办法,只好自己写一个了。其实在写之前我告诉了村长 Codebus 上面那个有 bug,村长建议我写一个发上去,在这样的怂恿下我于是才真正动手。
这一回其实也踩了点坑,算是还可以吧,也复习了一下旋转。我收获最大的就是发现要换个角度想问题,一开始我是从原图像映射到旋转后的图像上,这样会导致很多空点,效果不好,后来我还在想怎么通过附近的点去补这些空点……有点可笑。后来我上网查了才恍然大悟,可以从旋转后的图像往回找,反向映射到原图像上,这样就确保旋转后的图像中每个像素(除了多边形外面的补充像素)都可以在原图像中进行对应了,就解决了空点的问题。
好了,看看代码吧:
#include <easyx.h> #include <math.h> // 引用该库才能使用 AlphaBlend 函数 #pragma comment( lib, "MSIMG32.LIB") // 圆周率 // 旋转函数未使用圆周率,只是在示例代码中用到 #define PI 3.1415926 // 旋转图像(保留透明信息,自适应大小) // pImg 原图像 // radian 旋转弧度 // bkcolor 背景填充颜色 // 返回旋转后的图像 IMAGE RotateImage_Alpha(IMAGE* pImg, double radian, COLORREF bkcolor = BLACK) { radian = -radian; // 由于 y 轴翻转,旋转角度需要变负 float fSin = (float)sin(radian), fCos = (float)cos(radian); // 存储三角函数值 float fNSin = (float)sin(-radian), fNCos = (float)cos(-radian); int left = 0, top = 0, right = 0, bottom = 0; // 旋转后图像顶点 int w = pImg->getwidth(), h = pImg->getheight(); DWORD* pBuf = GetImageBuffer(pImg); POINT points[4] = { {0, 0}, {w, 0}, {0, h}, {w, h} }; // 存储图像顶点 for (int j = 0; j < 4; j++) // 旋转图像顶点,搜索旋转后的图像边界 { points[j] = { (int)(points[j].x * fCos - points[j].y * fSin), (int)(points[j].x * fSin + points[j].y * fCos) }; if (points[j].x < points[left].x) left = j; if (points[j].y > points[top].y) top = j; if (points[j].x > points[right].x) right = j; if (points[j].y < points[bottom].y) bottom = j; } int nw = points[right].x - points[left].x; // 旋转后的图像尺寸 int nh = points[top].y - points[bottom].y; int nSize = nw * nh; int offset_x = points[left].x < 0 ? points[left].x : 0; // 旋转后图像超出第一象限的位移(据此调整图像位置) int offset_y = points[bottom].y < 0 ? points[bottom].y : 0; IMAGE img(nw, nh); DWORD* pNewBuf = GetImageBuffer(&img); if (bkcolor != BLACK) // 设置图像背景色 for (int i = 0; i < nSize; i++) pNewBuf[i] = BGR(bkcolor); for (int i = offset_x, ni = 0; ni < nw; i++, ni++) // i 用于映射原图像坐标,ni 用于定位旋转后图像坐标 { for (int j = offset_y, nj = 0; nj < nh; j++, nj++) { int nx = (int)(i * fNCos - j * fNSin); // 从旋转后的图像坐标向原图像坐标映射 int ny = (int)(i * fNSin + j * fNCos); if (nx >= 0 && nx < w && ny >= 0 && ny < h) // 若目标映射在原图像范围内,则拷贝色值 pNewBuf[nj * nw + ni] = pBuf[ny * w + nx]; } } return img; } // 透明贴图(by 慢羊羊) void transparentimage(IMAGE* dstimg, int x, int y, IMAGE* srcimg) { HDC dstDC = GetImageHDC(dstimg); HDC srcDC = GetImageHDC(srcimg); int w = srcimg->getwidth(); int h = srcimg->getheight(); BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf); } // 使用示例 int main() { initgraph(320, 240); setbkcolor(YELLOW); setlinecolor(GREEN); BeginBatchDraw(); IMAGE imgPng, imgRotate; loadimage(&imgPng, L"sheep.png"); // 加载透明图片 for (double f = -2 * PI; f <= 2 * PI; f += 0.03) { cleardevice(); // 绘制背景 for (int y = 0; y < 240; y += 10) line(0, y, 320, y); imgRotate = RotateImage_Alpha(&imgPng, f, 0x66AA0000); // 图像旋转(设置了半透明的填充背景) transparentimage(NULL, 0, 0, &imgRotate); // 透明贴图 FlushBatchDraw(); Sleep (10); } flushmessage(); getmessage(EM_KEY); // 按任意键退出 closegraph(); return 0; }