首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 任意角度旋轉位圖 (BMP) 完整演算法與實作

     編輯:VC 教學

BMP 檔案結構速覽

  1. `BITMAPFILEHEADER` (14 bytes):檔案類型 `BM`、大小、資料偏移。
  2. `BITMAPINFOHEADER` (40 bytes):寬/高、位元深度 (24-bit)、壓縮方式、調色板資訊。
  3. 像素資料:按行儲存,每行 4-byte 對齊。

完整實作步驟

1. 讀取 BMP 到記憶體


CFile file; file.Open(path, CFile::modeRead);
BITMAPFILEHEADER bfh; file.Read(&bfh, sizeof(bfh));
BITMAPINFOHEADER bih; file.Read(&bih, sizeof(bih));
int w = bih.biWidth, h = bih.biHeight;
DWORD dataSize = bfh.bfSize - bfh.bfOffBits;
BYTE* pSrcBits = new BYTE[dataSize];
file.ReadHuge(pSrcBits, dataSize); file.Close();

2. 建立記憶體 DC 並繪製原圖


CClientDC dc(this);
HDC hMemSrc = CreateCompatibleDC(dc.m_hDC);
HBITMAP hBmp = CreateCompatibleBitmap(dc.m_hDC, w, h);
SelectObject(hMemSrc, hBmp);
BitBlt(hMemSrc, 0, 0, w, h, dc.m_hDC, 0, 0, SRCCOPY); // 將螢幕內容複製到記憶體位圖 (示範用)
// 實際應用: 用 SetDIBits 將 pSrcBits 直接寫入 hBmp

3. 旋轉核心算法 (逆向映射 + 雙線性插值可選)


void RotateAnyAngle(HDC hSrc, int srcW, int srcH, double angle, HDC hDst) {
    double sinA = sin(angle), cosA = cos(angle);
    // 計算旋轉後邊界
    double x1 = -srcH * sinA, y1 = srcH * cosA;
    double x2 = srcW * cosA - srcH * sinA, y2 = srcH * cosA + srcW * sinA;
    double x3 = srcW * cosA, y3 = srcW * sinA;
    double minX = min(0.0, min(x1, min(x2, x3)));
    double maxX = max(0.0, max(x1, max(x2, x3)));
    double minY = min(0.0, min(y1, min(y2, y3)));
    double maxY = max(0.0, max(y1, max(y2, y3)));
    int dstW = (int)ceil(maxX - minX), dstH = (int)ceil(maxY - minY);

    // 建立目標記憶體 DC
    HDC hMemDst = CreateCompatibleDC(hSrc);
    HBITMAP hDstBmp = CreateCompatibleBitmap(hSrc, dstW, dstH);
    SelectObject(hMemDst, hDstBmp);

    // 逆向映射:遍歷目標像素,反推源座標
    for (int dy = 0; dy < dstH; ++dy) {
        for (int dx = 0; dx < dstW; ++dx) {
            double sx = (dx + minX) * cosA + (dy + minY) * sinA;
            double sy = (dy + minY) * cosA - (dx + minX) * sinA;
            if (sx >= 0 && sx < srcW && sy >= 0 && sy < srcH) {
                // 最近鄰採樣 (簡單快速)
                BitBlt(hMemDst, dx, dy, 1, 1, hSrc, (int)sx, (int)sy, SRCCOPY);
                // 進階:雙線性插值需自行 GetPixel/SetPixel 或操作 DIB bits
            }
        }
    }
    // 輸出到螢幕或儲存
    BitBlt(hDst, 200, 200, dstW, dstH, hMemDst, 0, 0, SRCCOPY);
    DeleteObject(hDstBmp); DeleteDC(hMemDst);
}

4. 呼叫範例


double angle = 45.0 * 3.1415926 / 180.0; // 45 度 -> 弧度
RotateAnyAngle(hMemSrc, w, h, angle, dc.m_hDC);

優化方向

  • **雙線性插值**:計算四鄰域像素加權平均,消除鋸齒。
  • **DIB 直接操作**:鎖定 `BITMAP` bits 指標,避免 `BitBlt` 單像素呼叫開銷,速度提升 100 倍以上。
  • **SIMD/GPU**:大圖旋轉建議使用 GDI+ `Graphics::RotateTransform` 或 Direct2D/GPU Shader。

常見坑點

  1. 角度單位:C/C++ `sin/cos` 接受**弧度**,度數需 `* PI / 180`。
  2. 座標系:Windows GDI 原點左上,Y 向下;數學旋轉公式假設 Y 向上,需對 `sinA` 取負或調整公式。
  3. 目標 DC 尺寸不足導致裁切:必須依旋轉後外接矩形建立足夠大的位圖。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved