win7 64 :
使用 vs2010 建立一个 MFC的对话框工程,
然后添加一个按钮,给按钮添加一个点击事件 函数:
代码如下:
void CbmpTestDlg::OnBnClickedBtnSelBmp()
{
// TODO: 在此添加控件通知处理程序代码
CString strFileName;
CBitmap ccBitmap;
LPTSTR lpstrPath = NULL;
TCHAR tszTempPath[_MAX_PATH];
ZeroMemory(tszTempPath, MAX_PATH);
GetCurrentDirectory(MAX_PATH, tszTempPath);
lpstrPath = tszTempPath;
CFileDialog filedlg(TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_T("图像(*.bmp)|*.bmp"));
filedlg.m_ofn.lpstrInitialDir=lpstrPath ; //打开当前文件夹所在目录
if (filedlg.DoModal() == IDOK)
strFileName = filedlg.GetFileName();
else
return;
HBITMAP hBitmap = (HBITMAP) LoadImage(NULL, strFileName, IMAGE_BITMAP,
0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
//CBitmap m_bitmap;
ccBitmap.Attach(hBitmap);
CDC dcImage;
CDC *pDC = GetDC();
if (!dcImage.CreateCompatibleDC(pDC))
return;
BITMAP bm;
ccBitmap.GetBitmap(&bm);
// show the image.
dcImage.SelectObject(&ccBitmap);
pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcImage, 0, 0, SRCCOPY);
}
显示效果如下:
代码工程下载链接:
https://download.csdn.net/download/wowocpp/10496032
遇到问题
1 如何 在MFC中打开 console:
AllocConsole();
SetConsoleTitle(_T("debug console"));
freopen("CONOUT$","w",stdout);
printf("Hello\r\n");
2 如何 FileDialog 默认打开的是当前的exe所在的目录:
LPTSTR lpstrPath = NULL;
TCHAR tszTempPath[_MAX_PATH];
ZeroMemory(tszTempPath, MAX_PATH);
GetCurrentDirectory(MAX_PATH, tszTempPath);
lpstrPath = tszTempPath;
CFileDialog filedlg(TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_T("图像(*.bmp)|*.bmp"));
filedlg.m_ofn.lpstrInitialDir=lpstrPath ; //打开当前文件夹所在目录
if (filedlg.DoModal() == IDOK)
strFileName = filedlg.GetFileName();
else
return;
注意:
该程序并不能显示所有的bmp格式的图片。
具体原因是:
LoadImage 这个函数有问题:
LoadImage 加载 Bmp 文件失败之我的理解!
http://www.cctry.com/thread-278857-1-1.html
QQ截图之后的BMP图片名字是:qq.bmp,之后我用 mspaint 将 qq.bmp 另存为 24位位图的 ok.bmp
我分别对这两个文件按照 bmp 的文件格式将文件头读出来。关于 bmp 的文件格式,大家网上找下,有很多说明的,bmp文件的第一个文件头结构是:BITMAPFILEHEADER,占用 14 个字节,第二个文件头结构是:BITMAPINFOHEADER,占用 40 个字节。
BITMAPFILEHEADER bitmap_file_header = { 0 };
int file_header_len = sizeof(BITMAPFILEHEADER);
BITMAPINFOHEADER bitmap_info_header = { 0 };
int file_info_len = sizeof(BITMAPINFOHEADER);
CFile mFile;
BOOL bRet = mFile.Open(strImgFile, CFile::modeReadWrite);
bRet = mFile.Read(&bitmap_file_header, file_header_len);
bRet = mFile.Read(&bitmap_info_header, file_info_len);
mFile.Close();
看到了吧,bitmap_info_header 的 biHeight 成员,qq.bmp 的是 -275,ok.bmp 的是正的 275。差别就在这。
于是到网上找关于 biHeight 是负数的情况。找到的资料如下:
图像的高度可以为负值。负数值表示接下来的像素内容将从左上角从左到右、从上到下读取;而正数值表示接下来的像素内容将从右下角从左到右、从下到上读取。位图信息头BITMAPINFOHEADER中的 biHeight 不仅体现位图高度,还标记此位图的存储方式。对于一幅位图:
位图信息头BITMAPINFOHEADER中的biHeight不仅体现位图高度,还标记此位图的存储方式。对于一幅位图:
高度为正
高度为负
此类情况模式常用的场合是:用户自己产生一幅图像,譬如在数据采集系统中常常需要将数据进行图像显示。这时用户需要开辟一块内存并按一定方式填充该内存。由于顺序存储方式对用户来说更加直观,操作更加方便,所有常使用第二种模式。这时,在显示和保存位图时将BITMAPINFOHEADER中的biHeight设为负数即可。
这回明白了吧。看来还是 LoadImage 支持的不够好啊。。。好了原因找到了,解决方案也就有了,大家可以自己写一个转换函数,将 Top-Down 与 Bottom-Up 相互转换。如果嫌麻烦的话可以直接使用其他的一些现成的类,比如 ATL 中的 CImage,或者开源的 CxImage 来加载任意格式的图片文件。他们都能无缝对接 BITMAP 对象。方便在 VC 中使用啊。
遇到过这种情况 索性直接用CXImage了
参考文档:
关于BMP
http://blog.csdn.net/u010839382/article/details/51576335
Top-Down vs. Bottom-Up DIBs
https://msdn.microsoft.com/en-us … 07212(v=vs.85).aspx
LoadImage() with QRCode bitmap failing unless file is opened/saved with MS Paint first
https://stackoverflow.com/questi … saved-with-ms-paint
BITMAPINFOHEADER structure
https://msdn.microsoft.com/en-us … 18229(v=vs.85).aspx
BMP文件格式详解(BMP file format)
https://www.cnblogs.com/Matrix_Y … /12/02/1615295.html
参考
https://support.microsoft.com/zh-cn/help/158898/howto-how-to-use-loadimage-to-read-a-bmp-file
如何: 如何使用 LoadImage() 来读取BMP 文件
LoadImage API 可用于从 BMP 文件加载位图。但是,它不返回调色板信息。这篇文章提供了示例代码,并描述如何使用LoadImage检索位图的调色板信息。
查看原始的英语文章:158898
概要
LoadImage API 可用于从 BMP 文件加载位图。但是,它不返回调色板信息。这篇文章提供了示例代码,并描述如何使用LoadImage检索位图的调色板信息。
详细信息
下面的代码使用 LoadImage API 来加载该位图作为 DIBSection,然后从 DIBSection 的颜色表中创建一个调色板。如果颜色表不存在,则使用半色调调色板:
BOOL LoadBitmapFromBMPFile( LPTSTR szFileName, HBITMAP *phBitmap,
HPALETTE *phPalette )
{
BITMAP bm;
*phBitmap = NULL;
*phPalette = NULL;
// Use LoadImage() to get the image loaded into a DIBSection
*phBitmap = (HBITMAP)LoadImage( NULL, szFileName, IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE );
if( *phBitmap == NULL )
return FALSE;
// Get the color depth of the DIBSection
GetObject(*phBitmap, sizeof(BITMAP), &bm );
// If the DIBSection is 256 color or less, it has a color table
if( ( bm.bmBitsPixel * bm.bmPlanes ) <= 8 )
{
HDC hMemDC;
HBITMAP hOldBitmap;
RGBQUAD rgb[256];
LPLOGPALETTE pLogPal;
WORD i;
// Create a memory DC and select the DIBSection into it
hMemDC = CreateCompatibleDC( NULL );
hOldBitmap = (HBITMAP)SelectObject( hMemDC, *phBitmap );
// Get the DIBSection's color table
GetDIBColorTable( hMemDC, 0, 256, rgb );
// Create a palette from the color tabl
pLogPal = (LOGPALETTE *)malloc( sizeof(LOGPALETTE) + (256*sizeof(PALETTEENTRY)) );
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = 256;
for(i=0;i<256;i++)
{
pLogPal->palPalEntry[i].peRed = rgb[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = rgb[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = rgb[i].rgbBlue;
pLogPal->palPalEntry[i].peFlags = 0;
}
*phPalette = CreatePalette( pLogPal );
// Clean up
free( pLogPal );
SelectObject( hMemDC, hOldBitmap );
DeleteDC( hMemDC );
}
else // It has no color table, so use a halftone palette
{
HDC hRefDC;
hRefDC = GetDC( NULL );
*phPalette = CreateHalftonePalette( hRefDC );
ReleaseDC( NULL, hRefDC );
}
return TRUE;
}
下面的代码演示如何使用LoadBitmapFromBMPFile函数:
case WM_PAINT:
{
PAINTSTRUCT ps;
HBITMAP hBitmap, hOldBitmap;
HPALETTE hPalette, hOldPalette;
HDC hDC, hMemDC;
BITMAP bm;
hDC = BeginPaint( hWnd, &ps );
if( LoadBitmapFromBMPFile( szFileName, &hBitmap, &hPalette ) )
{
GetObject( hBitmap, sizeof(BITMAP), &bm );
hMemDC = CreateCompatibleDC( hDC );
hOldBitmap = (HBITMAP)SelectObject( hMemDC, hBitmap );
hOldPalette = SelectPalette( hDC, hPalette, FALSE );
RealizePalette( hDC );
BitBlt( hDC, 0, 0, bm.bmWidth, bm.bmHeight,
hMemDC, 0, 0, SRCCOPY );
SelectObject( hMemDC, hOldBitmap );
DeleteObject( hBitmap );
SelectPalette( hDC, hOldPalette, FALSE );
DeleteObject( hPalette );
}
EndPaint( hWnd, &ps );
}
break;