MFC中从位图角度旋转图片示例代码

MFC中从位图角度旋转图片示例代码

cpp 复制代码
// 角度宏:0=不旋转,1=顺时针90°,2=顺时针180°,3=顺时针270°
#define ROTATE_0   0
#define ROTATE_90  1
#define ROTATE_180 2
#define ROTATE_270 3

/**
 * @brief 固定角度旋转位图(仅 0/90/180/270 度),纯内存像素操作。
 *
 * 本函数将输入 CBitmap 顺时针旋转到 0/90/180/270 四种角度之一。
 * 内部使用 32bpp 顶向下 DIBSection 做像素搬运,不依赖 MFC 的 CDC。
 *
 * @param pInput  输入位图指针,不能为空。
 * @param nRotate 旋转角度宏:ROTATE_0 / ROTATE_90 / ROTATE_180 / ROTATE_270。
 * @return  旋转后的 CBitmap(内部附加了新的 HBITMAP)。若失败,返回空 CBitmap。
 *
 * @note
 * - 源位图将通过 GetDIBits 转换为 32bpp 顶向下格式进行处理。
 * - 返回值为临时对象时,在老编译器/老 MFC 下可能存在所有权不清风险;
 *   更稳妥的做法是使用带 out 参数的重载(见下文)。
 */
CBitmap RotateBitmap(const CBitmap* pInput, int nRotate)
{
    if (!pInput)
        return CBitmap();

    // 读取源位图信息
    BITMAP bm{};
    if (!pInput->GetBitmap(&bm) || bm.bmWidth <= 0 || bm.bmHeight <= 0)
        return CBitmap();

    // 仅允许 0/1/2/3
    if (nRotate < ROTATE_0 || nRotate > ROTATE_270)
        nRotate = ROTATE_0;

    // --- 创建源 DIBSection(32bpp,顶向下)并填充像素 ---
    BITMAPINFO biSrc{};
    biSrc.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    biSrc.bmiHeader.biWidth       = bm.bmWidth;
    biSrc.bmiHeader.biHeight      = -bm.bmHeight;    // 顶向下
    biSrc.bmiHeader.biPlanes      = 1;
    biSrc.bmiHeader.biBitCount    = 32;
    biSrc.bmiHeader.biCompression = BI_RGB;

    void*    pBitsSrc = nullptr;
    HBITMAP  hDibSrc  = ::CreateDIBSection(nullptr, &biSrc, DIB_RGB_COLORS, &pBitsSrc, nullptr, 0);
    if (!hDibSrc)
        return CBitmap();

    HDC hdc = ::GetDC(nullptr); // 仅用于 GetDIBits
    if (!hdc) {
        ::DeleteObject(hDibSrc);
        return CBitmap();
    }

    const HBITMAP hInput = (HBITMAP)pInput->GetSafeHandle();
    const int gotLines = ::GetDIBits(hdc, hInput, 0, bm.bmHeight, pBitsSrc, &biSrc, DIB_RGB_COLORS);
    ::ReleaseDC(nullptr, hdc);

    if (gotLines == 0) { // 失败
        ::DeleteObject(hDibSrc);
        return CBitmap();
    }

    DWORD* src = static_cast<DWORD*>(pBitsSrc);

    // --- 计算目标尺寸 ---
    int newW = bm.bmWidth;
    int newH = bm.bmHeight;
    if (nRotate == ROTATE_90 || nRotate == ROTATE_270) {
        newW = bm.bmHeight;
        newH = bm.bmWidth;
    }

    // --- 创建目标 DIBSection(32bpp,顶向下) ---
    BITMAPINFO biDst{};
    biDst.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    biDst.bmiHeader.biWidth       = newW;
    biDst.bmiHeader.biHeight      = -newH;
    biDst.bmiHeader.biPlanes      = 1;
    biDst.bmiHeader.biBitCount    = 32;
    biDst.bmiHeader.biCompression = BI_RGB;

    void*   pBitsDst = nullptr;
    HBITMAP hDibDst  = ::CreateDIBSection(nullptr, &biDst, DIB_RGB_COLORS, &pBitsDst, nullptr, 0);
    if (!hDibDst) {
        ::DeleteObject(hDibSrc);
        return CBitmap();
    }
    DWORD* dst = static_cast<DWORD*>(pBitsDst);

    // --- 各角度像素搬运 ---
    switch (nRotate)
    {
    case ROTATE_0:
        // 逐行快速拷贝
        for (int y = 0; y < bm.bmHeight; ++y) {
            ::memcpy(dst + y * newW, src + y * bm.bmWidth, bm.bmWidth * sizeof(DWORD));
        }
        break;

    case ROTATE_90: // x' = H-1-y, y' = x
        for (int y = 0; y < bm.bmHeight; ++y) {
            for (int x = 0; x < bm.bmWidth; ++x) {
                const int x2 = newW - 1 - y; // H-1 - y
                const int y2 = x;
                dst[y2 * newW + x2] = src[y * bm.bmWidth + x];
            }
        }
        break;

    case ROTATE_180: // x' = W-1-x, y' = H-1-y
        for (int y = 0; y < bm.bmHeight; ++y) {
            for (int x = 0; x < bm.bmWidth; ++x) {
                const int x2 = newW - 1 - x;   // W-1 - x
                const int y2 = newH - 1 - y;  // H-1 - y
                dst[y2 * newW + x2] = src[y * bm.bmWidth + x];
            }
        }
        break;

    case ROTATE_270: // x' = y, y' = W-1-x
        for (int y = 0; y < bm.bmHeight; ++y) {
            for (int x = 0; x < bm.bmWidth; ++x) {
                const int x2 = y;
                const int y2 = bm.bmWidth - 1 - x; // W-1 - x
                dst[y2 * newW + x2] = src[y * bm.bmWidth + x];
            }
        }
        break;
    }

    // 清理源 DIB
    ::DeleteObject(hDibSrc);

    // 封装返回
    CBitmap bmpDst;
    bmpDst.Attach(hDibDst);
    return bmpDst; // 注意:按值返回在老 MFC 下可能有所有权语义风险,见注记
}


/**
 * @brief 固定角度旋转位图(0/90/180/270),结果输出到 out,规避按值返回风险。
 * @return TRUE 成功,FALSE 失败
 */
BOOL RotateBitmap(const CBitmap* pInput, int nRotate, CBitmap& out)
{
    CBitmap tmp = RotateBitmap(pInput, nRotate);
    if (tmp.GetSafeHandle() == nullptr)
        return FALSE;

    // 把句柄"转移"给 out,避免双重释放
    out.Attach((HBITMAP)tmp.Detach());
    return TRUE;
}
相关推荐
_Twink1e12 小时前
openJiuwen 实训营 Day 1 · WorkSwarm 入门教程
开发语言·c++·openjiuwen
dong_junshuai13 小时前
每天一个开源项目#87 fmt:24.5K Stars 的 C++ 格式化核心
c++·开源·github
雪的季节14 小时前
Multisim14.0的详细中文安装步骤【附安装包】
c++
Persistent的粽子!15 小时前
C++:类与对象(二)
开发语言·c++
蒸蒸yyyyzwd16 小时前
cpp 选手备战秋招学习笔记 day23
c++·求职招聘
.YM.Z16 小时前
C++ STL 容器深度剖析:vector 与 list 的模拟实现及对比
开发语言·c++·vector·list
mengzhi啊17 小时前
QEventLoop loop配合loop.exec();替代while(1)。电脑cpu占用为0,使用while(1)cpu占用率100%
c++·qt
HugoStudio_SWAN17 小时前
洛谷 P1319 / P1320 压缩技术——同一枚硬币的正反面
c++·学习·程序人生·算法
小小晓.18 小时前
C++单例模式万字详解:6种实现演进+线程安全彻底解决+CRTP终极模板
c++·单例模式
j7~19 小时前
【C++11】C++11 新特性全套详解(列表初始化、右值引用、lambda 表达式、function 与 bind 包装器等)
c++·右值引用·lambda表达式·可变参数模板·移动语义·包装器·c++11发展史