- 浏览: 14618 次
- 性别:
- 来自: 上海
最新评论
BMP文件加载探讨(转http://blog.sina.com.cn/s/blog_5da62ae00100pyls.html)
- 博客分类:
- 技术杂绘
BMP文件加载探讨(转http://blog.sina.com.cn/s/blog_5da62ae00100pyls.html)
2011年10月09日
通常都是用loadimage把*.bmp文件加载到内存,然后进行处理。不过,这次在加载大的图形文件时候,却出现了像闹鬼式的失败,察看失败代码是8,ERROR_NOT_ENOUGH_MEMORY。而且是有时候失败,有时候又不失败。这才专门花上了些功夫,终于把原因自以为找了出来。
简而言之,就是DDB和DIB图像处理上的区别。明显的区别就不多说了,造成上面描述问题的,是DDB资源管理方式造成的。加载到内存的DDB资源,特别是 bitmap,其实并不是放在应用程序能够申请到的空间。Loadimage把它load的东西据称放到一个desktop heap的地方。这我没有办法确认,只能够微软说啥就是啥吧。
那我就要处理多个大的bmp图片怎么办?请看我下面描述的第三种方法吧。
方法一:
////直接从外部文件加载图片
HBITMAP bitmap;
bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),strFileName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
m_backBitmap.DeleteObject();
if(!m_backBitmap.Attach(bitmap))
{
MessageBox("导入背景图失败!","提示",MB_OK);
return;
}
****************************************
void CitemView::getBitMap( CDC *pDC )
{
CDC MemDC;
HBITMAP hBmp;
BITMAP bm;
CBitmap Bitmap;
CPoint point( 10, 10);
CString cStr;
//hBmp = (HBITMAP)::LoadImage(NULL,"BG.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),"BG.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
Bitmap.DeleteObject();
Bitmap.Attach( hBmp );
Bitmap.GetObject(sizeof(BITMAP),&bm);
MemDC.CreateCompatibleDC(pDC);
MemDC.SelectObject(&Bitmap);
pDC->BitBlt(point.x, point.y, bm.bmWidth, bm.bmHeight, &MemDC, 0, 0, SRCCOPY);
MemDC.DeleteDC();
}
方法二:
用::CreateDIBitmap。呵呵,这个函数其实该叫Create DDB from DIBitmap更好。
Step1:打开BMP文件
CFile * t_pfile=new CFile;
t_bool=t_pfile->Open(arg_mapfile , CFile::modeRead | CFile::typeBinary) ;
m_vDIBDataLength=t_pfile->GetLength();
m_pDIBData= new char [m_vDIBDataLength];
t_dwCount=t_pfile->Read(m_pDIBData,m_vDIBDataLength);
m_lpBMFH=(LPBITMAPFILEHEADER)m_pDIBData;
m_lpBMIH=(LPBITMAPINFOHEADER)(m_pDIBData+sizeof(BITMAPFILEHEADER));
m_lpBMI=(LPBITMAPINFO)m_lpBMIH;
m_vBits=m_lpBMIH->biBitCount;
m_lpData=(LPSTR)m_lpBMFH+m_lpBMFH->bfOffBits;
上面这段伪代码就把BMP文件调入内存了,并把读入内存的文件依照结构分为BITMAP File Header,Bitmap Info Header,Bitmap Info,和真正的数据区m_lpData。
Step2:由这个DIB的文件创建对应的DDB图片:
::CreateDIBitmap(pDc->GetSafeHdc(), m_lpBMIH, CBM_INIT, m_lpData, m_lpBMI, DIB_RGB_COLORS);
说到这里,特别强调一下,上面这种方法,并不是打开一个BMP,或JPG的好方法。
先说一下Windows里面图像的两种表示方法:DDB和DIB。
DDB可以理解为设备相关,也就是windows在其特定设备上的内部的图像表示方法。有些地方也叫GDI图像,因为GDI相关的函数都是对DDB的图像直接进行操作。这原本也没有什么问题。最大的问题是:根据我的经验,系统内部的GDI资源(或DDB的图像资源),如icon,鼠标,toolbar,等,都是放在特定的内存区域。这个区域还不能够扩展到你所拥有的全部内存。于是,当你用上面的方法打开*.bmp文件,特别是文件还比较大的时候,没几个你就会发现向你报告内存不够。
用GetLastError通常你会得到是8,ERROR_NOT_ENOUGH_MEMORY。可是你查看你的内存,还大大的空!
这个问题不管你是用LoadImage,直接得到HBITMAP,还是打开文件,然后把文件读取到内存,再用转化为DDB,你都会遇到这个问题。更惨的是,如果调试或程序中有这样的DDB图片内存没有释放,那么以后你就再也没有足够内存来打开。LoadImage和CreateDIBitmap都会返回NULL。这时候,你只能够冲启机器了。
具体原因我也说不清楚,但有一点可以确定,就是DDB的图像资源不是和程序资源放在一起,而是放在系统中的某个角落里。有一篇文件曾经提到过desktop heap,但我不能够确定。最明显的现象就是,当你把图片转化为DDB后,你可以查看你的程序内存占用情况,和系统总的内存被消耗情况,就能够发现区别了。
总而言之,放置DDB(或GDI图像)的区域比较特殊,不是你有多少内存,就能够申请多少的。
方法三:
接下去的问题就是:我的程序就是需要打开多张大图,怎么办?其实在方法二中答案已经有了。
图片文件不是都已经全部读到内存了么?这部分内存是程序申请的,有多大的内存,就可以申请多大。只不过,这时候的图片都是DIB形式,不能够直接赋值给CBitmap,也不能够直接被CDC对象使用。如果这些图片需要被显示出来,就直接用位图操作函数,把图片映射到device context上:
::SetDIBitsToDevice或:: StretchDIBits。
SetDIBitsToDevice中有两个参数不是很清楚:
UINT uStartScan, // first scan line in array
UINT cScanLines, // number of scan lines
但是第一个设置为0,第二个设置为位图高度就OK了。
一小段代码例证如下:
::SetDIBitsToDevice(dc.GetSafeHdc(),UpdateRect.left,UpdateRect.top,UpdateRect.Width(),UpdateRect.Height(), UpdateRect.left, t_pdoc->m_vMapSize.cy-UpdateRect.top-UpdateRect.Height(), 0,(WORD)m_lpBMIH->biHeight, m_lpData,m_lpBMI,DIB_RGB_COLORS);
这一小段代码可以接和方法二中的代码理解。特别要强调的是:Windows的窗口坐标是左上为原点,而通常的DIB图片是左下为原点。这在绘制无效区域的时候,需要特别注意期间的转换。
方法四:
如果我还要支持GIF,JPG怎么办?
那就用IPicture的接口吧。
当然有很多讲述这个接口怎么使用的文章。我这里就简单把代码亮出来,并特别强调一下注意事项。
1,打开文件,老生常谈。
CFile * t_pfile=new CFile;
t_bool=t_pfile->Open(arg_mapfile , CFile::modeRead | CFile::typeBinary) ;
2,读取文件到内存。注意,这里没有用new来分配内存,而是用GlobalAlloc,是因为后面的创建IStream时候需要。
int m_vDIBDataLength=t_pfile->GetLength();
HGLOBAL m_hDIBData=GlobalAlloc(GMEM_MOVEABLE,m_vDIBDataLength);
char* m_pDIBData= (char*)GlobalLock(m_hDIBData);
t_pfile->Read(m_pDIBData,m_vDIBDataLength);
3,创建一个IStream对象。为什么?便于后面创建IPicture。把图片载入刀IPicture。
IStream *t_pStm=NULL;
IPicture* m_pJPGPic=NULL;
CreateStreamOnHGlobal(m_hDIBData, TRUE, &t_pStm);
if(FAILED(OleLoadPicture(t_pStm,m_vDIBDataLength,TRUE,IID_IPicture,(LPVOID*)&m_pJPGPic))) {
MessageBox("导入背景图失败!-- Invalide Bitmap file","提示",MB_OK);
goto t_error3;
}
4, 释放Istream资源,file资源。图片资源保留,在后面绘图用。
if(NULL!=t_pStm){ // jpg has been loaded, and the file resouce can be removed.
t_pStm->Release();
t_pStm=NULL;
m_pDIBData=NULL;// 在下面通过handle被释放掉了。
GlobalUnlock(m_hDIBData);
GlobalFree(m_hDIBData);
m_hDIBData=NULL;
}
t_pfile->Close();
delete t_pfile; t_pfile=NULL;
5,在OnPaint函数中,用Ipicture进行绘图。
m_pJPGPic->Render(dc.GetSafeHdc(),0,0,t_pdoc->m_vMapSize.cx,t_pdoc->m_vMapSize.cy,
0,t_mapheight,t_mapwidth,-t_mapheight,NULL);
就这个函数的参数含义需要特别说明。这个函数的参数中,有两个坐标体系,而且图片的映射方式也和前面3种方法提到的BitBlt,SetDIBitsToDevice不一样。
首先,看src的参数。x,y,cx,cy。这个参数是MM_TEXT体系下的坐标点和长度。从OnPaint函数的CDC中得到无效区域坐标可以直接拿来用。
然后再看dst的参数。x,y,cx,cy。dst的参数是HIMETRIC体系下的值。这是要注意的第一点。
这个render函数要注意的第二点是,它会把src和dst设置的原点映射到一起。而clientDC中的起点是左上,文件的图片体系中,通常原点是左下。所以src和dst的原点必须保持一致。这就是大家可以看到,dst中的高度设置的是个负值。
注意了上面两点,基本上就没有什么问题了。
6,程序退出的时候,别忘了释放IPicture。
if(NULL!=m_pJPGPic){
m_pJPGPic->Release();
m_pJPGPic=NULL;
}
发表评论
-
linux下编程实现mplayer播放器总结
2012-01-20 00:13 1017linux下编程实现mplayer播放器总结 2011年05 ... -
掌握 Linux 调试技术 +Debian/Ubuntu内核编程者必备
2012-01-20 00:13 688掌握 Linux 调试技术 +Debian/Ubuntu内核编 ... -
操作系统学习笔记1
2012-01-20 00:13 674操作系统学习笔记1 2010 ... -
深入理解控制台程序
2012-01-20 00:13 878深入理解控制台程序 2010年06月26日 在Delph ... -
Java IO 技术之基本流类
2012-01-20 00:13 526Java IO 技术之基本流类 ... -
C/C++ > UNIX系统程序设计
2012-01-19 01:05 639C/C++ > UNIX系统程序设 ... -
C段错误总结
2012-01-19 01:05 624C段错误总结 2012年01月07日 最近一段时间在li ... -
liniux学习
2012-01-19 01:05 847liniux学习 2010年06月08日 ... -
PNG文件结构(PNG图片格式)详解(转)
2012-01-19 01:04 1335PNG文件结构(PNG图片格式 ... -
delphi中opengl程序设计
2012-01-19 01:04 652delphi中opengl程序设计 2011年06月07日 ... -
bmp保存
2012-01-17 00:28 354bmp保存 2010年10月24日 CFileDialo ... -
使用jpeglib,实现jpg和bmp互转
2012-01-17 00:27 1604使用jpeglib,实现jpg和bmp互转 2011年10月 ... -
BMP文件操作方法(一个月研究出来的)
2012-01-17 00:27 702BMP文件操作方法(一个月研究出来的) 2011年07月20 ... -
BMP位图文件的存储格式3
2012-01-17 00:27 650BMP位图文件的存储格式3 2011年06月18日 1. ... -
很久很久以前的事
2012-01-16 13:53 735很久很久以前的事 2010年08月01日 今天给自己的大 ... -
2011-9-15
2012-01-16 13:53 5082011-9-15 2011年09月15日 第 1课时 ... -
如何培养一年级学生良好的学习习惯
2012-01-16 13:53 512如何培养一年级学生良好的学习习惯 2011年06月28日 ... -
各位家长非常辛苦,其他老师也很辛苦,孩子也很辛苦。希望我们相互理解,用朋友的角度去商量让孩子变的好起来
2012-01-16 13:53 550各位家长非常辛苦,其他 ... -
日记那点破事儿
2012-01-16 13:53 329日记那点破事儿 2011年0 ...
相关推荐
资源分类:Python库 所属语言:Python 资源全名:pyls_livepy-0.2.0-py3-none-any.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
python库。 资源全名:pyls_memestra-0.0.5-py3-none-any.whl
pip3 install trepan3k autopep8 python-language-server pyls-mypy pyls-isort pyls-black jupyter mccabe pydocstyle pyflakes pylint rope flake8-mypy ptvsd -i https://mirrors.aliyun.com/pypi/simple/ ...
快速开始 git clone https://github.com/sanskrit/data.git && cd datapython bin/make_data.pyls all-data数据来自多个来源,每个来源都有自己的格式。 make_data.py将所有数据转换为通用格式,并将结果存储在all-...
python与pyls , rust用rust-mode 安装clang工具:苹果系统: brew install llvm 添加到PATH中.zshenv (不.zshrc ),例如这样的: export PATH=/usr/local/bin:/usr/bin:/binexport PATH= " $HOME /.cargo/bin: $...
Python JSON RPC服务器 协议的Python 2.7和3.4+服务器实现。 该库已从项目中撤出。 使用Python 3的concurrent.futures模块和Python 支持异步请求处理。安装pip install -U python-jsonrpc-server 例子examples目录...
突出显示语法 模块和函数文档字符串 PyDocStyle和PyCodeStyle标志 McCabe圈复杂度 更改时自动重新加载首选项(某些首选项仍需要重新加载完整的扩展名)。安装使用以下命令安装依赖项: pip3 install '
现今ABA受体已被公认为RCARs/PYR/PYLs家族蛋白.本实验通过拟南芥为模式植物,利用酵母双杂交系统共转化方法从拟南芥cDNA文库中筛选与 RCAR3相互作用的蛋白质,获得多个阳性克隆,从中选取一个与生长素调节相关的基因 ...
python lsp支持的pyls 。设置将此~/.config/nvim克隆到~/.config/nvim 打开neovim: nvim 打开后,它将下载一个名为vim-plug ,让其运行完成。 只要下载此文件,您就可以保持精明,如果发生任何其他错误,请不要...
语言服务器协议覆盖 语言服务器列表 艾尔斯 钓鱼者 巴塞尔 豆数 ccls clojure_lsp cmake 代码库 cssls 达尔特斯 烯醇 dhall_lsp_server 诊断 ... html ... pyls_ms 吡咯 r_language_serve
IDE-python软件包 由提供的Python语言支持,由。 要求 需要 , 和基于软件包才能公开Atom中的... 您可以通过python -m pyls --help运行python -m pyls --help来验证是否已正确安装一切。 它应该返回 usage: pyls [-h