將繪圖導向記憶體裝置內容 (Memory DC) 配合裝置無關位圖 (DIB),繪製完成後直接讀取位元資料寫入 BMP 檔案結構。關鍵步驟:
CBitmapBITMAPFILEHEADER + BITMAPINFOHEADERvoid 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);
}

CreateDIBSection 直接取得像素緩衝區指標 pBits,免去 GetDIBits 複製開銷