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;
}