bitmap保存到文件和原图不一样

cqruan 2017-11-23 05:20:52
我有一张BMP 8位位图如下:


把此位图加入到资源,然后用代码进行保存

HBITMAP hBitmap=NULL;
/*
if(::OpenClipboard(m_hWnd) && ::IsClipboardFormatAvailable(CF_BITMAP)){
hBitmap=(HBITMAP)::GetClipboardData(CF_BITMAP);
if(!hBitmap)return;
::CloseClipboard();
}
*/

CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
hBitmap=bitmap;

BITMAP bm={0};
GetObject(hBitmap,sizeof(bm),(void**)&bm);
BITMAPINFOHEADER bih={0};
bih.biSize=sizeof(BITMAPINFOHEADER);
bih.biWidth=bm.bmWidth;
bih.biHeight=-bm.bmHeight;
bih.biPlanes=bm.bmPlanes;
bih.biBitCount=8;
bih.biCompression=BI_RGB;

if(bih.biWidth==0 || bih.biHeight==0){
AfxMessageBox("HBITMAP无效");
return;
}

int nColors=0;
DWORD cbPalette=0;
PALETTEENTRY *lpPaletteEnry=NULL;

HDC hDC=GetDC()->GetSafeHdc();
HPALETTE hPalette=NULL,hOldPalette=NULL;
//BYTE *biData=(BYTE*)malloc(abs(bih.biHeight)*bih.biWidth*sizeof(BYTE));

if(bih.biBitCount<=8){
nColors=1<<bih.biBitCount;
cbPalette=nColors*sizeof(PALETTEENTRY);//调色板的大小

LOGPALETTE lgPalette={0};
lgPalette.palVersion=0x300;
lgPalette.palNumEntries=nColors;

//创建调色板
hPalette=::CreatePalette(&lgPalette);
lpPaletteEnry=new PALETTEENTRY[nColors];
memset(lpPaletteEnry,0,sizeof(PALETTEENTRY)*nColors);

//扫描位图获得颜色表
HDC hMemoDC=::CreateCompatibleDC(NULL);
HBITMAP hOldBitmap=(HBITMAP)SelectObject(hMemoDC,hBitmap);

BYTE r,g,b;
BOOL bExists;COLORREF clr=0;int nPos=0;
ULONG *lpTemp=new ULONG[nColors],*lp;
memset(lpTemp,0,sizeof(ULONG)*nColors);

int index=0;
for(int y=0;y<bm.bmHeight;y++){
for(int x=0;x<bm.bmWidth;x++){
clr=GetPixel(hMemoDC,y,x);
r=GetRValue(clr);
g=GetGValue(clr);
b=GetBValue(clr);

index=0;
bExists=FALSE;
lp=lpTemp;
while(*lp && (nPos<nColors) ){
if(*(lp++)==clr){
bExists=TRUE;
break;
}else{
index++;
}
}

if(!bExists && nPos<nColors){
*lp=clr;
lpPaletteEnry[nPos].peRed=r;
lpPaletteEnry[nPos].peGreen=g;
lpPaletteEnry[nPos].peBlue=b;
nPos++;
}

//*(biData+(bm.bmHeight-y-1)*bm.bmWidth+x)=index;//上下对调,效果与GetDIBis相同
}
}

::SelectObject(hMemoDC,hOldBitmap);
::DeleteDC(hMemoDC);
//获取颜色表结束

::SetPaletteEntries(hPalette,0,nColors,lpPaletteEnry);//设置调色板的颜色表
hOldPalette=::SelectPalette(hDC,hPalette,FALSE);//选入设备
::RealizePalette(hDC);//实现调色板
}

DWORD cbClrData=(bm.bmWidth*bih.biBitCount+31)/8/4*4*bm.bmHeight;
DWORD cbData=sizeof(BITMAPINFOHEADER)+cbPalette+cbClrData;
BYTE *lpbi=new BYTE[cbData];
memset(lpbi,0,cbData);
bih.biSizeImage=cbClrData;
*((BITMAPINFOHEADER*)lpbi)=bih;

UINT iFlag=DIB_PAL_COLORS;
if(bih.biBitCount<=8){
iFlag=DIB_PAL_COLORS;
}
int lines=::GetDIBits(hDC,hBitmap,0,bm.bmHeight,lpbi+sizeof(BITMAPINFOHEADER)+cbPalette,(BITMAPINFO*)lpbi,iFlag);
if(lines!=bm.bmHeight){
AfxMessageBox("获取图像位失败");
return;
}

if(bih.biBitCount<=8){
//GetDIBits会对调色板区域进行乱填充数据
memcpy(lpbi+sizeof(BITMAPINFOHEADER),lpPaletteEnry,cbPalette);
}

if(hOldPalette){
::SelectPalette(hDC,hOldPalette,TRUE);
::DeleteObject(hPalette);
}

BITMAPFILEHEADER bfh={0};
bfh.bfType='MB';
bfh.bfSize=sizeof(BITMAPFILEHEADER)+cbData;
bfh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+cbPalette;

CFile file;
if(!file.Open("bitmap.bmp",CFile::modeWrite|CFile::modeCreate)){
AfxMessageBox("创建文件失败");
return;
}

file.WriteHuge(&bfh,sizeof(BITMAPFILEHEADER));
file.SeekToEnd();
file.WriteHuge(lpbi,cbData);
file.Flush();
file.Close();


得到的新图如下:

另外发现,GetDIBits 函数会对lpbi的调色板内存进行修改,通过EditPlus的16进制方式查看,他修改的颜色表是固定的字符串。

注意这是个8位位图,GetDIBits 最后一个参数是用DIB_PAL_COLORS,不是用DIB_RGB_COLORS.
如果要得到和原图一样的,请问应该怎么做?谢谢。

...全文
413 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
cqruan 2017-11-29
  • 打赏
  • 举报
回复
引用 12 楼 hongwenjun 的回复:
非常感谢您,BMP存储为BRG顺序,这句话是关键,我把调色板数据在存储之前改成了BGR,结果完美解决,感谢! 也感谢赵老师,感谢参与的同学们!
hongwenjun 2017-11-29
  • 打赏
  • 举报
回复
cqruan 2017-11-27
  • 打赏
  • 举报
回复
好,我再试试!谢谢各位!
赵4老师 2017-11-25
  • 打赏
  • 举报
回复
用一幅美女图再测试一下你的程序。
cqruan 2017-11-24
  • 打赏
  • 举报
回复
我刚才又测试了一遍,如果把RGB改成BGR,结果是一样的。没有任何变化。 我发现结果图并非和原图全部上下颠倒,而是左边一半颠倒了,有趣的是,如果我把结果图按照本方法加载再保存一次,就得到最初的原图了。
cqruan 2017-11-24
  • 打赏
  • 举报
回复
引用 4 楼 paschen 的回复:
保存成与设备无关位图
你好,谢谢你的参与,PALETTEENTRY 的结构是RGB的,他跟RGBQUAD结构不同。 同时非常感谢赵老师。麻烦您帮我瞧瞧呢。
赵4老师 2017-11-24
  • 打赏
  • 举报
回复
引用 5 楼 jiht594 的回复:
RGB-->BGR?
jiht594 2017-11-24
  • 打赏
  • 举报
回复
RGB-->BGR?
jiht594 2017-11-24
  • 打赏
  • 举报
回复
引用 8 楼 cqruan 的回复:
我刚才又测试了一遍,如果把RGB改成BGR,结果是一样的。没有任何变化。
我发现结果图并非和原图全部上下颠倒,而是左边一半颠倒了,有趣的是,如果我把结果图按照本方法加载再保存一次,就得到最初的原图了。


你没发现是R和B颠倒了吗?
paschen 版主 2017-11-23
  • 打赏
  • 举报
回复
保存成与设备无关位图
cqruan 2017-11-23
  • 打赏
  • 举报
回复
赵老是,很感谢你,它值颠倒了1半边。另外一半边是好的,我测试了各种。实在找不出办法了。
赵4老师 2017-11-23
  • 打赏
  • 举报
回复
或者百度搜“保存带调色板bitmap”
赵4老师 2017-11-23
  • 打赏
  • 举报
回复
百度搜“bitmap上下颠倒”

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧