保留透明(Alpha)信息地旋转图像
2022-7-19
(0)
前言
事实上我并未打算造这个轮子,因为 Lost 已经实现了一个相同功能的函数,见 文章。
Lost 实现的函数确实可用,但存在致命 bug,因此我也在他的文章下留言回复,并私信了他,但由于他暂时没有回复,我便自行实现了一个。
原理
其实不用多说原理,只是基本的二维坐标旋转,此处不再赘述,如有兴趣,可以必应。
唯一需要注意的就是,映射坐标的时候,应该从旋转后的图像去对应原图像,否则反过来的话,由于像素密度不够,旋转出的图像会出现“空点”。
代码
注意:运行示例代码前,请在项目目录下放置一张 png 图像,并记得在 main 函数中确认您的图片路径。或者可以用下面这张,并命名为 sheep.png 放在项目目录下。
#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;
}
添加评论
取消回复