使用GDI +載入JPG和PNG資源的CGdiPlusBitmap類
介紹
最近我需要顯示一些JPG和PNG檔案。我有一箇舊版的LeadTools和這兩種格式的開源庫,但希望我的可執行檔案儘可能的小。所以我決定給GDI +一個嘗試。我很快發現GDI +設計不好,非常古怪,但它對我的目的很好,直到我發現我的恐懼,GDI +無法載入儲存為資源的JPG或PNG影象!
像我相信,其他開發人員面臨這個問題,我不相信文件,試圖Bitmap::FromResource
無濟於事。在瀏覽可用的Bitmap方法的同時,我碰到過Bitmap::FromStream
。
經過一些測試和幾個錯誤,主要是由於可怕的GDI +文件,我想出了工作程式碼。休息一晚後,我決定將程式碼封裝在一個簡單的類中,以確保記憶體被釋放。CGdiPlusBitmap
和CGdiPlusBitmapResource
。
Gotcha
在討論程式碼本身之前,有一個需要解決的GDI +的警告。使用JPG,某些TIFF和其他格式,原始影象資訊必須始終可用。換句話說,如果您開啟點陣圖Bitmap::FromFile
,則在影象開啟時您不能刪除或以其他方式更改該檔案。同樣的限制適用於CGdiPlusBitmapResource
。(我的測試發現,PNG和BMP檔案似乎沒有遵循這一概括,雖然我不知道這是標準行為還是與我的檔案只是一個僥倖。)
GDI +初始化
在進行任何GDI +呼叫之前,需要初始化GDI +。我建議將以下資料成員新增到派生自CWinApp
:
ULONG_PTR m_gdiplusToken;
在InitInstance()
,新增以下呼叫:
Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
在ExitInstance()
,新增以下內容:
Gdiplus::GdiplusShutdown(m_gdiplusToken);
類
我建立了兩個類,基類是非常簡單的封裝 Bitmap
和封裝全域性記憶體的派生類。我想,如果我有耐心和願望,我可以延長這個類,但我沒有必要這樣做。CImage
,那是因為程式碼被用在一個沒有使用MFC或ATL的程式中,但程式碼很簡單,很容易被修改為使用CImage
作為基類。)
我不會干擾CGdiPlusBitmap
類
,除非說它有一個公共的資料成員Bitmap* m_pBitmap
。(在類中,我使用Gdiplus
名稱空間來開發GDI
+物件,以防開發人員不想宣告using namespace
Gdiplus;
。)
本CGdiPlusBitmapResource
類有幾個建構函式和幾個過載Load
功能。過載功能簡單地允許像我一樣的惰性程式設計師不需要鍵入MAKEINTRESOURCE
。主Load
函式將資源名稱和型別作為字串,並且是類的關鍵。本程式碼全部如下:
inline bool CGdiPlusBitmapResource::Load(LPCTSTR pName, LPCTSTR pType, HMODULE hInst) { Empty(); HRSRC hResource = ::FindResource(hInst, pName, pType); if (!hResource) return false; DWORD imageSize = ::SizeofResource(hInst, hResource); if (!imageSize) return false; const void* pResourceData = ::LockResource(::LoadResource(hInst, hResource)); if (!pResourceData) return false; m_hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize); if (m_hBuffer) { void* pBuffer = ::GlobalLock(m_hBuffer); if (pBuffer) { CopyMemory(pBuffer, pResourceData, imageSize); IStream* pStream = NULL; if (::CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK) { m_pBitmap = Gdiplus::Bitmap::FromStream(pStream); pStream->Release(); if (m_pBitmap) { if (m_pBitmap->GetLastStatus() == Gdiplus::Ok) return true; delete m_pBitmap; m_pBitmap = NULL; } } m_pBitmap = NULL; ::GlobalUnlock(m_hBuffer); } ::GlobalFree(m_hBuffer); m_hBuffer = NULL; } return false; }
我發現程式碼自作聰明,雖然知道返回值的::LoadResource
是一個HGLOBAL
可能會發現明顯的雙重複制使用CopyMemory
混亂。簡而言之,CreateStreamOnHGlobal
需要使用標誌HGLOBAL
分配的控制代碼。通過GlobalAlloc
使用
GMEM_MOVEABLE
標誌