huidong

首页 | 会员登录 | 关于争取 2022 寒假做出汇东网 Ver3.0.0 !
搜索文章


旋转图像最关键的就是坐标旋转公式,其实这个东西很简单,可以了解: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;
}




返回首页


Copyright (C) 2018-2024 huidong