重溫WIN32 API ------ 視窗上繪製點陣圖檔案
阿新 • • 發佈:2019-02-19
1 基本思路
做介面模擬時,經常需要在視窗上貼圖,隨著圖片數量的增多,通過資原始檔中新增點陣圖的方式會變得越來越不可控。所以本著“資源與程式分離“的原則,還是使用外部點陣圖檔案更加清晰明瞭。
那麼如何管理點陣圖的貼圖位置呢?如果寫死到程式程式碼中,則又會導致耦合行過高的問題。容易想到解決方法有兩個,一是使用一個單獨的xml檔案來記錄圖片檔名稱和貼圖位置的關係,二是直接把貼圖位置資訊包含進點陣圖檔案的檔名中。本文采用更加簡單的後者,點陣圖檔名格式規範為:description_xxx-yyy.bmp ,其中xxx為貼圖時相對於所在視窗的x座標,yyy為貼圖時相對於所在視窗的y座標,例如:發動機按鈕_100-200.bmp,表示貼圖時,目的座標點為(100,200)。
至於貼圖的實現,考慮到只需要支援bmp一種格式即可,所以採用GDI庫完成。考慮到一個位圖檔案可能會被貼圖多次(例如重新整理的時候),所以實現時沒有直接SetDIBitsToDevice(),而是首先把DIB通過CreateDIBitmap()轉化為DDB,然後儲存這個DDB,這樣以後每次貼圖時,只需要BitBlt()這個DDB就可以了,提高了效率。
2 程式碼實現
BitmapHelper.h
#pragma once /******************************************************************************** BitmapHelper 貼圖助手 功能描述: 根據點陣圖檔名,把點陣圖檔案讀入並貼到指定視窗,為提高效率物件內部一直 儲存讀入記憶體的BITMAP,所以只在第一次貼圖時需要從外部檔案讀取。 使用說明: 每一個位圖檔案對應一個BitmapHelper類物件。使用樣例: BitmapHelper *pBmp = new BitmapHelper(L"D:\\風景_100-300.bmp"); pBmp->ShowOnWindow(this->m_hWnd); delete pBmp; // 程式結束或不再需要這個點陣圖時,刪除 ********************************************************************************/ class BitmapHelper { public: BitmapHelper(TCHAR* file); ~BitmapHelper(); protected: TCHAR fileName[256] ; // 點陣圖檔名 HBITMAP hBitmap; // 點陣圖控制代碼 int desX; // 目的x座標 int desY; // 目的y座標 protected: void ShowOnWindow(HWND hwnd, int x, int y); // 在指定視窗上顯示 public: HBITMAP CreateBitmapObjectFromDibFile(HDC hdc); //從檔案中獲取DDB void ShowOnWindow(HWND hwnd); void ShowOnDevice(HDC dc, int x, int y); };
BitmapHelper.cpp
#include "stdafx.h" #include "BitmapHelper.h" #include "string.h" BitmapHelper::BitmapHelper(TCHAR* file) { this->hBitmap = NULL; this->desX = -9999; this->desY = -9999; ::StrCpyNW(this->fileName, file, 256); this->fileName[255] = TEXT('\0'); } /* 功能:從點陣圖檔案建立DDB 引數: hdc 裝置DC 返回值: DDB控制代碼,錯誤返回NULL */ HBITMAP BitmapHelper::CreateBitmapObjectFromDibFile(HDC hdc) { BITMAPFILEHEADER* pbmfh = NULL; HANDLE hFile = NULL; DWORD dwFileSize = 0; hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile == INVALID_HANDLE_VALUE) { return NULL; } dwFileSize = GetFileSize(hFile, NULL); pbmfh = (BITMAPFILEHEADER*)malloc(dwFileSize); if (pbmfh == NULL) { CloseHandle(hFile); return NULL; } DWORD dwBytesRead = 0; BOOL bSuccess = ::ReadFile(hFile, pbmfh, dwFileSize, &dwBytesRead, NULL); ::CloseHandle(hFile); // 驗證檔案確實是點陣圖檔案 if (!bSuccess || dwBytesRead != dwFileSize || pbmfh->bfType != *(WORD*) "BM" || pbmfh->bfSize != dwFileSize) { free(pbmfh); return NULL; } HBITMAP hBitmap = ::CreateDIBitmap(hdc, (BITMAPINFOHEADER*)(pbmfh + 1), CBM_INIT, (BYTE*)pbmfh + pbmfh->bfOffBits, (BITMAPINFO*)(pbmfh + 1), DIB_RGB_COLORS); free(pbmfh); return hBitmap; } /* 功能:在指定的裝置上顯示圖片 引數:hdc 裝置DC, (x,y)為目的左上角座標 */ void BitmapHelper::ShowOnDevice(HDC hdc, int x, int y) { if (this->hBitmap == NULL) { this->hBitmap = this->CreateBitmapObjectFromDibFile(hdc); } if (this->hBitmap != NULL) { BITMAP bitmap; ::GetObject(this->hBitmap, sizeof(BITMAP), &bitmap); HDC hdcMem = ::CreateCompatibleDC(hdc); ::SelectObject(hdcMem, this->hBitmap); ::BitBlt(hdc, x, y, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY); ::DeleteDC(hdcMem); } } /* 功能:在指定視窗上顯示點陣圖 引數:hwnd 視窗控制代碼, (x,y) 目的左上角座標 */ void BitmapHelper::ShowOnWindow(HWND hwnd, int x, int y) { if (this->hBitmap == NULL) { HDC hdc = ::GetDC(hwnd); this->hBitmap = this->CreateBitmapObjectFromDibFile(hdc); ::ReleaseDC(hwnd, hdc); } if (this->hBitmap != NULL) { BITMAP bitmap; ::GetObject(this->hBitmap, sizeof(BITMAP), &bitmap); HDC hdc = ::GetDC(hwnd); HDC hdcMem = ::CreateCompatibleDC(hdc); ::SelectObject(hdcMem, this->hBitmap); ::BitBlt(hdc, x, y, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY); ::DeleteDC(hdcMem); ::ReleaseDC(hwnd, hdc); } } /* 功能:根據檔名解析位置顯示點陣圖檔案到指定視窗 引數: hwnd 目的視窗 說明: 點陣圖檔案命名規範 name_xxx-yyy.bmp */ void BitmapHelper::ShowOnWindow(HWND hwnd) { // 解析檔名 if (this->desX == -9999) { int i = 0; int indexLast_ = 0; //最後一個_,表示座標的開始 int indexLastDot = 0; //最後一個.,表示副檔名的開始 int indexSep = 0; // 座標分割標誌- int n = wcslen(this->fileName); for (i = n-1; i >=0; i--) { if (this->fileName[i] == L'_') { break; } } indexLast_ = (i == 0 ? -1 : i); // -1 表示沒有目錄部分 for (i = n - 1; i >= 0; i--) { if (this->fileName[i] == L'.') { break; } } indexLastDot = (i == 0 ? n : i); // n表示沒有副檔名部分 int xyStart = indexLast_ + 1; // 座標起始位置 int xyEnd = indexLastDot - 1; // 座標結束位置 for (i = xyStart; i <= xyEnd; i++) { if (this->fileName[i] == L'-') { break; } } indexSep = i == xyEnd ? -1 : i; // -1 表示沒有找到- if (n==0 || indexSep==-1) { this->desX = 0; this->desY = 0; } else { TCHAR s_x[10]; int count = indexSep - xyStart; wcsncpy_s(s_x, 10, this->fileName+xyStart, count); s_x[count] = L'\0'; this->desX = _wtoi(s_x); WCHAR s_y[10]; count = xyEnd - indexSep; wcsncpy_s(s_y, 10, this->fileName+indexSep+1, count); s_y[count] = L'\0'; this->desY = _wtoi(s_y); } } this->ShowOnWindow(hwnd, this->desX, this->desY); } BitmapHelper::~BitmapHelper() { if (this->hBitmap != NULL) // 清理點陣圖資源 { ::DeleteObject(this->hBitmap); } }