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

VC++ 自繪圖形輸出 BMP 檔案:記憶體 DC 與 DIB 完整實作

     編輯:關於VC++

核心原理

將繪圖導向記憶體裝置內容 (Memory DC) 配合裝置無關位圖 (DIB),繪製完成後直接讀取位元資料寫入 BMP 檔案結構。關鍵步驟:

  1. 建立相容記憶體 DC
  2. 建立 DIB Section 並關聯 CBitmap
  3. 選入記憶體 DC 進行繪圖
  4. 填寫 BITMAPFILEHEADER + BITMAPINFOHEADER
  5. 寫入像素資料至檔案

完整實作代碼

void CTestSaveBmpView::SaveAsBmp(CString filename) {
    // 1. 圖片參數
    int iWidth = 800, iHeight = 600, iPixel = 16; // 16-bit 色深

    // 2. 設定 BITMAPINFOHEADER
    LPBITMAPINFO lpbmih = new BITMAPINFO;
    lpbmih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    lpbmih->bmiHeader.biWidth = iWidth;
    lpbmih->bmiHeader.biHeight = iHeight;
    lpbmih->bmiHeader.biPlanes = 1;
    lpbmih->bmiHeader.biBitCount = iPixel;
    lpbmih->bmiHeader.biCompression = BI_RGB;
    lpbmih->bmiHeader.biSizeImage = 0;
    lpbmih->bmiHeader.biXPelsPerMeter = 0;
    lpbmih->bmiHeader.biYPelsPerMeter = 0;
    lpbmih->bmiHeader.biClrUsed = 0;
    lpbmih->bmiHeader.biClrImportant = 0;

    // 3. 建立記憶體 DC 與 DIB Section
    HDC hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
    HDC hdcMem = CreateCompatibleDC(hdc);
    BYTE* pBits = NULL;
    HBITMAP hBitMap = CreateDIBSection(hdcMem, lpbmih, DIB_PAL_COLORS, (void**)&pBits, NULL, 0);

    CBitmap* pBitMap = new CBitmap;
    pBitMap->Attach(hBitMap);
    CDC* pMemDC = new CDC;
    pMemDC->Attach(hdcMem);
    pMemDC->SelectObject(pBitMap);

    // 4. 繪製內容 (背景 + 曲線)
    CRect rc(0, 0, iWidth, iHeight);
    pMemDC->SetBkMode(TRANSPARENT);
    DrawCurve(pMemDC, rc);  // 自訂繪圖函式

    // 5. 寫入 BMP 檔案
    BITMAPFILEHEADER bmfh;
    ZeroMemory(&bmfh, sizeof(BITMAPFILEHEADER));
    *((char*)&bmfh.bfType) = 'B';
    *(((char*)&bmfh.bfType) + 1) = 'M';
    bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmfh.bfSize = bmfh.bfOffBits + (iWidth * iHeight) * iPixel / 8;

    TCHAR szBMPFileName[128];
    int iBMPBytes = iWidth * iHeight * iPixel / 8;
    strcpy(szBMPFileName, filename);
    CFile file;
    if (file.Open(szBMPFileName, CFile::modeWrite | CFile::modeCreate)) {
        file.Write(&bmfh, sizeof(BITMAPFILEHEADER));
        file.Write(&(lpbmih->bmiHeader), sizeof(BITMAPINFOHEADER));
        file.Write(pBits, iBMPBytes);
        file.Close();
    }

    // 6. 清理資源
    pMemDC->DeleteDC(); delete pMemDC; pMemDC = NULL;
    delete pBitMap; pBitMap = NULL;
    delete lpbmih; lpbmih = NULL;
}

繪圖範例:函數曲線

void CTestSaveBmpView::DrawCurve(CDC* pDC, CRect rcClient) {
    // 白底
    CBrush brushCtl; brushCtl.CreateSolidBrush(RGB(255,255,255));
    pDC->Rectangle(rcClient); pDC->FillRect(rcClient, &brushCtl); brushCtl.DeleteObject();

    // 紅色曲線 y = 600/x
    CPen pen; pen.CreatePen(PS_SOLID, 1, RGB(255,0,0));
    CPen* oldPen = pDC->SelectObject(&pen);

    double xMin=10, xMax=100, yMin=10, yMax=200;
    double dbX1 = (xMax-xMin)/100 + xMin;
    double dbY1 = 600/dbX1;

    for (int i=1; i<100; i++) {
        double dbX2 = (xMax-xMin)*i/100 + xMin;
        double dbY2 = 600/dbX2;

        pDC->MoveTo(int(rcClient.left + (dbX1-xMin)*rcClient.Width()/(xMax-xMin)),
                    int(rcClient.bottom - (dbY1-yMin)*rcClient.Height()/(yMax-yMin)));
        pDC->LineTo(int(rcClient.left + (dbX2-xMin)*rcClient.Width()/(xMax-xMin)),
                    int(rcClient.bottom - (dbY2-yMin)*rcClient.Height()/(yMax-yMin)));

        dbX1 = dbX2; dbY1 = dbY2;
    }
    pDC->SelectObject(oldPen); pen.DeleteObject();
}

觸發儲存:滑鼠右鍵選單

void CTestSaveBmpView::OnRButtonDown(UINT nFlags, CPoint point) {
    CFileDialog dlg(FALSE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
        _T("位圖文件(*.bmp)|*.bmp|"), NULL);
    if (dlg.DoModal() == IDOK) {
        CString filename = dlg.GetFileName() + _T(".bmp");
        SaveAsBmp(filename);
    }
    CView::OnRButtonDown(nFlags, point);
}

效果展示

輸出 BMP 效果圖

關鍵技術點總結

  • CreateDIBSection 直接取得像素緩衝區指標 pBits,免去 GetDIBits 複製開銷
  • 16-bit 色深 (RGB565) 檔案較小,若需真彩色請改用 24/32-bit 並處理行對齊 (4 位元組倍數)
  • 記憶體 DC 繪圖支援所有 GDI 函式,可輸出文字、幾何圖形、位圖混合內容
  • 資源釋放順序:DC → Bitmap → BITMAPINFO,避免 GDI 物件洩漏
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved