最近遇到一个问题,需要将Bitmap中的图像Buffer导出来。
有人说用GDI的GetDIBits方法,有人说用GDI+的LockBits方法。
找了很多资料,都没找到可直接运行的代码,只有老老实实的查MSDN。
最后终于搞定了,下面见代码:
// BitmapToByteArray.cpp : Defines the entry point for the console application.
//
#include <tchar.h>
#include <windows.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus")
using namespace Gdiplus;
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
void ExtractBitmapToByteArray_1()
{
// Create a Bitmap object from a BMP file.
Bitmap bitmap(_T("menu.png"));
BitmapData bmpData;
Rect rect(0, 0, bitmap.GetWidth(), bitmap.GetHeight());
bitmap.LockBits(
&rect,
ImageLockModeWrite,
PixelFormat32bppARGB,
&bmpData);
// Write to the temporary buffer provided by LockBits.
UINT* pixels = (UINT*)bmpData.Scan0;
int byteCount = bmpData.Stride * bmpData.Height;
BYTE* pBuffer = new BYTE[byteCount];
memcpy(pBuffer, pixels, byteCount);
Bitmap bitmap22(
bmpData.Width,
bmpData.Height,
bmpData.Stride,
bmpData.PixelFormat,
(BYTE*)bmpData.Scan0);
CLSID clsid;
GetEncoderClsid(_T("image/png"), &clsid);
bitmap22.Save(_T("menu_1.png"), &clsid);
delete []pBuffer;
bitmap.UnlockBits(&bmpData);
}
// 通过临时文件将png图片保存到IStream中
BOOL ExtractBitmapToByteArray_2()
{
Bitmap bitmap(_T("menu.png"));
TCHAR pszPath[MAX_PATH] = {0};
GetTempPath(MAX_PATH, pszPath);
const TCHAR tmpName[] = _T("menu.png.tmp");
lstrcat(pszPath, tmpName);
IStorage* pstgFile = NULL;
HRESULT hr = ::StgCreateDocfile(pszPath,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0, &pstgFile);
if( FAILED(hr) ) { return FALSE; }
IStream * pStream = NULL;
TCHAR pszName[10] = _T("Image");
hr = pstgFile->OpenStream(pszName, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, &pStream);
if (FAILED(hr)) {
hr = pstgFile->CreateStream(pszName, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream);
if (FAILED(hr)) { return FALSE; }
}
// 以png格式保存到IStream中
CLSID ImageClsid;
GetEncoderClsid(_T("image/png"), &ImageClsid);
bitmap.Save(pStream, &ImageClsid);
// Begin -- HH Debug
STATSTG statstg;
pStream->Stat(&statstg, STATFLAG_NONAME);
int nDatalen = (ULONG)statstg.cbSize.QuadPart;
// End -- HH Debug
// read
GetEncoderClsid(_T("image/png"), &ImageClsid);
Gdiplus::Bitmap* pRead = Gdiplus::Bitmap::FromStream(pStream);
pRead->Save(_T("menu2.png"), &ImageClsid);
pStream->Release();
pstgFile->Release();
DeleteFile(pszPath);
return TRUE;
}
//------------保存Bitmap到文件---------------
BOOL SaveBitmapToFile(HBITMAP hBitmap, LPCTSTR lpFileName )
{
HDC hDC; //设备描述表
int iBits; //当前显示分辨率下每个像素所占字节数
WORD wBitCount; //位图中每个像素所占字节数
DWORD dwPaletteSize=0, //定义调色板大小, 位图中像素字节大小 ,位图文件大小 , 写入文件字节数
dwBmBitsSize,
dwDIBSize, dwWritten;
BITMAP Bitmap; //位图属性结构
BITMAPFILEHEADER bmfHdr; //位图文件头结构
BITMAPINFOHEADER bi; //位图信息头结构
LPBITMAPINFOHEADER lpbi; //指向位图信息头结构
HANDLE fh, hDib, hPal,hOldPal=NULL; //定义文件,分配内存句柄,调色板句柄
//计算位图文件每个像素所占字节数
HDC hWndDC = CreateDC(_T("DISPLAY"),NULL,NULL,NULL);
hDC = ::CreateCompatibleDC( hWndDC ) ;
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
if (iBits <= 1)
wBitCount = 1;
else if (iBits <= 4)
wBitCount = 4;
else if (iBits <= 8)
wBitCount = 8;
else if (iBits <= 24)
wBitCount = 24;
else
wBitCount = 24 ;
//计算调色板大小
if (wBitCount <= 8)
dwPaletteSize = (1 << wBitCount) * sizeof(RGBQUAD);
//设置位图信息头结构
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmBitsSize = ((Bitmap.bmWidth * wBitCount+31)/32) * 4 * Bitmap.bmHeight ;
//为位图内容分配内存
hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
// 处理调色板
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hDC = ::GetDC(NULL);
hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);
RealizePalette(hDC);
}
// 获取该调色板下新的像素值
GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,
(LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
+dwPaletteSize,
(LPBITMAPINFO )
lpbi, DIB_RGB_COLORS);
//恢复调色板
if (hOldPal)
{
SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
//创建位图文件
fh = CreateFile(lpFileName, GENERIC_WRITE,
0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (fh == INVALID_HANDLE_VALUE)
return FALSE;
// 设置位图文件头
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize = sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER)
+ dwPaletteSize + dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)
+ (DWORD)sizeof(BITMAPINFOHEADER)
+ dwPaletteSize;
// 写入位图文件头
WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
// 写入位图文件其余内容
WriteFile(fh, (LPSTR)lpbi, dwDIBSize,
&dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(fh);
return TRUE;
}
void ExtractBitmapToByteArray_3()
{
Bitmap bmp(_T("menu.png"));
HBITMAP hBitmap;
bmp.GetHBITMAP(Color(0,255,255,255), &hBitmap);
SaveBitmapToFile(hBitmap, _T("menu_3.bmp"));
}
int _tmain(int argc, _TCHAR* argv[])
{
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
ExtractBitmapToByteArray_1();
GdiplusShutdown(gdiplusToken);
return 0;
}
一共有2种方法将png保存到图片中。
第3种是将png保存到bmp中。