利用 GDI+ 从资源文件加载和缩放 PNG

之前实现了右键菜单(shellext context menu)功能,开始用的是 BMP 图标。不支持透明背景,也不支持缩放(高分屏下很小)。今天参考了一下同事实现的其他模块的代码,也从在网上搜了很多文章来看,终于实现了。代码实现如下:

bool LoadImageFromRes(Gdiplus::Bitmap *&hBitmap, HGLOBAL &hResourceBuffer, HMODULE hInstance, UINT resourceID)
{
	HRSRC hResource = ::FindResource(hInstance, MAKEINTRESOURCE(resourceID), L"PNG");
	if (!hResource)
		return false;
	DWORD imageSize = ::SizeofResource(hInstance, hResource);
	if (!imageSize)
		return false;
	const void* pResourceData = ::LockResource(::LoadResource(hInstance, hResource));
	if (!pResourceData)
		return false;
	hResourceBuffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
	if (hResourceBuffer)
	{
		void *pBuffer = ::GlobalLock(hResourceBuffer);
		if (pBuffer)
		{
			CopyMemory(pBuffer, pResourceData, imageSize);
			IStream *pStream = nullptr;
			if (::CreateStreamOnHGlobal(hResourceBuffer, FALSE, &pStream) == S_OK)
			{
				hBitmap = Gdiplus::Bitmap::FromStream(pStream);
				pStream->Release();
				if (hBitmap)
				{
					if ((hBitmap)->GetLastStatus() == Gdiplus::Ok)
						return true;
					delete hBitmap;
					hBitmap = nullptr;
				}
			}
			hBitmap = nullptr;
			::GlobalUnlock(hResourceBuffer);
		}
		::GlobalFree(hResourceBuffer);
		hResourceBuffer = nullptr;
	}
	return false;
}
Gdiplus::Bitmap *ResizeClone(Gdiplus::Bitmap *bitmap, INT width)
{
	UINT oldHeight = bitmap->GetHeight();
	UINT oldWidth = bitmap->GetWidth();
	INT newWidth = width;
	INT newHeight = static_cast<INT>(static_cast<double>(oldHeight) / oldWidth * newWidth);
	Gdiplus::Bitmap *newBitmap = new Gdiplus::Bitmap(newWidth, newHeight, bitmap->GetPixelFormat());
	Gdiplus::Graphics graphics(newBitmap);
	graphics.DrawImage(bitmap, 0, 0, newWidth, newHeight);
	return newBitmap;
}
HANDLE LoadPNG(HINSTANCE hInst, UINT resourceID, int width)
{
	Gdiplus::Bitmap *bitmap = nullptr;
	HGLOBAL hResourceBuffer = nullptr;
	if (LoadImageFromRes(bitmap, hResourceBuffer, hInst, resourceID))
	{
		if (bitmap->GetWidth() != width)
		{
			Gdiplus::Bitmap *newBitmap = Util::ResizeClone(bitmap, width);
			delete bitmap;
			bitmap = newBitmap;
		}
		HBITMAP hBmpIcon = nullptr;
		bitmap->GetHBITMAP(0, &hBmpIcon);
		delete bitmap;
		::GlobalUnlock(hResourceBuffer);
		::GlobalFree(hResourceBuffer);
		return hBmpIcon;
	}
	return nullptr;
}

第一段代码是从资源文件加载 PNG,如果直接从本地文件载的话,实现会简单很多,直接Gdiplus::Bitmap::FromFile 就可以了。
 
参考:

  1. Resizing an image with GDI+ with C++
  2. Loading JPG & PNG resources using GDI+
  3. Image Scaling with GDI+ Part 3: DrawImage() and the Settings that Affect It

发表回复