内存DC详细介绍
- 格式:doc
- 大小:102.00 KB
- 文档页数:21
GDI的内存泄露问题2011-01-08 22:15:17| 分类:VC使用技巧| 标签:gdi loadbitmap 泄露 imagehdc pdc |字号大中小订阅对于不停的获得和释放绘图设备句柄(DC)可能会导致GDI内存泄露。
本文从一个实际项目中总结出以下几条经验防止内存泄露。
1. 尽量使用HDC和DeleteDC组合,不要用CDC2.3. GetDC和ReleaseDC配合使用4.5. 对于常使用的图片资源,在程序初始化前就加载,并保存在内存里6.7. 声音文件的播放和释放第二部分在windows系列上做编程,gdi是一个很重要的技术点,有很多程序在运行多次后出现异常,除了众所周知的内存泄露以外,gdi资源泄露也是一个很直接的原因.今天就把我自己在编程中总结的一些经验给大家分享,欢迎高手补充.1.Create出来的gdi对象,一定要用DeleteObject来释放,释放顺序是先Create的后释放,后Create的先释放.这里的Create指的是以它为开头的gdi函数,比如,CreateDIBitmap,CreateFont等等,最后都要调用DeleteObject来释放.2.Create出来的dc要用DeleteDC来释放,Get到的要用ReleaseDC释放.3.确保释放DC的时候DC中的各gdi对象都不是你自己创建的;确保个gdi对象在释放的时候不被任何dc选中使用.假如我们要使用gdi函数画图,正确的步骤应该如下:a.创建一个内存兼容dc(CreateCompatibleDC)b.创建一个内存兼容bitmap(CreateCompatibleBitmap)c.关联创建的内存兼容dc和bitmap(SelectObject)d.画图e.BitBlt到目的dc上f.断开内存兼容dc和bitmap关联(SelectObject)g.销毁内存兼容bitmaph.销毁内存兼容dc由于SelectObject在选入一个新的gdi对象的时候会返回一个原来的gdi对象(假如成功的话),所以需要在步骤c的时候保存返回值,在步骤f的时候当作入口参数使用.还有,步骤g和步骤h实际上顺序可以随意,因为他们两个此刻已经没有关系了,但是为了结构清晰,我建议按照"先Create的后释放,后Create的先释放"的原则进行.关于步骤f,可能会有争议,因为即使省略这一步,步骤g和步骤h看起来照样可以返回一个成功的值.但实际上可能并没有执行成功,至少boundschecker会报告有错,错误信息大致是说,在释放dc的时候还包含有非默认的gdi对象,在释放gdi对象的时候又说这个gdi对象还被一个dc在使用.所以,我建议保留步骤f.4.关于98下使用CreateCompatibleBitmap按照msdn的说法,创建出来的size不能超过16m.实际情况是这样吗?非也~!从我自己做的测试结果来看(win98se-sc),这个值在2044*2043和2044*2044之间,然而,后来在另外一个98系统上这个值也不行,后来我干脆把上限给成了2000*2000.很幸运,到现在还没有出问题,但我不能保证这个数字就是正确的.还有一点,假如宽或高有一个超过32768,哪怕另外一个值是1,也会创建失败,有兴趣的可以自己做个测试.如果要想保证这个函数在98下永远成功,可以试试下面的代码:float factor = 10.f;while(!bitmap.CreateCompatibleBitmap(&dc ,nWidth*factor ,nHeight*factor)){factor -= 0.01f;}这样至少可以保证宽和高是成比例的:)5.关于在打印机上使用BitBlt有时候在内存兼容dc里面已经做好图了,但在使用BitBlt的时候却会失败.这个时候,首先确认创建的内存兼容dc和bitmap是不是使用打印机的dc,如果确认无误,还是执行BitBlt失败,那80%可能是内存兼容bitmap太大了,请按如下方法再试试:创建另外一个内存兼容dc2和一个比较小的内存兼容biimap2,大概是1000*1000吧,我是这样用的:)然后把dc里面的内容分成块(1000*1000),把每一块BitBlt到dc2上面,再从dc2里面BitBlt到打印dc上.有人可能会有这样的疑问:那为什么不直接把dc里面的内容分几次BitBlt 到打印机上呢?有区别吗?答案是肯定的,如果dc里面的bitmap太大,哪怕你想BitBlt一个10*10的区域到打印机上都会失败.第三部分第四部分利用任务管理器侦测GDI内存泄露2010-01-23 02:00:33| 分类:vc界面编程| 标签:|字号大中小订阅打开任务管理器,点击菜单“查看”——“选择列”,勾上所有项,“确定”。
VC程序设计课程设计报告书设计题目:黑白棋游戏设计院系:班级:组别:学号:姓名:起止日期:指导教师:目录前言 (1)1. 课程设计计划 (2)2.需求分析 (3)3.概要设计 (4)4.详细设计 (5)5.编码与测试 (13)6.心得体会 (15)参考文献 (16)前言黑白棋,也称反棋(Reversi)、奥赛罗棋(Othello),苹果棋,翻转棋。
游戏通过相互翻转对方的棋子,最后以棋盘上谁的棋子多来判断胜负。
它的游戏规则简单,因此上手很容易,但是它的变化又非常复杂。
起初,黑白棋在西方和日本很流行,近年来,黑白棋正逐步为中国玩家所接受,并深受喜爱。
本次我们组选择这个题目做大作业,一方面是巩固和提高以前所学的VC++的知识;另一方面是因为这可能是我们中的大多数人第一次完成一个软件的系统设计,选择一个中等难度且相对成熟的软件来设计既易于实现又留有一定的上升空间,符合我们的实际情况。
我们所设计的这种黑白棋游戏有几个特点:程序短小精悍,简洁明了;游戏界面美观,容易操作;功能丰富,趣味性强。
1. 课程设计计划黑白棋游戏设计所完成的就是一个游戏软件系统。
其一般的功能包括:开始新局,对弈方式选择,先手选择,棋力设置,限时设置,计算机演示,保存棋局,载入棋局,导出走棋信息,重温棋局,英雄榜,悔棋,设置属性,帮助信息,关于黑白棋的介绍等,要求设计一个黑白棋游戏软件,通过计算机实现各个功能。
其具体设计要求为:1、位图的导入和棋盘的生成2、记录双方对弈时间的计时器与记录双方子数的计数器3、选择人机对战与人人对战的函数及控制堆栈难度的主函数4、实现悔棋功能的堆栈我们对项目期望能实现a 可以实现人机对弈。
b 棋力可调,分初级,中级,高级。
c 提示音功能,下错、下对有不同的提示音。
d 玩家遇到疑问,需要帮助时,给予一定的帮助e 界面色彩友好,给人以良好的视觉冲击。
f 操作方便,容易上手。
本课程设计工作进度计划为:表1 课程设计计划本课程设计任务的分工:陈涛涛完成人机对战中的实现保存棋局、载入棋局、重温棋局、英雄榜、悔棋功能。
提高二维矢量绘图效率之一般做法作者:朱金灿如何提高二维矢量绘图效率?这个问题很普遍。
最近在研究这个问题,在网上搜了一些资料,再结合自己的经验,谈谈自己的一些想法。
一.双缓存能提高绘图效率吗?网上有篇文章:绘图效率完整解决方案——三种手段提高GDI/GDI+绘图效率,其中提到一种方法是:1. 缓存——Bitmap或者DoubleBuffer。
缓存就是先把绘制的图形绘制到一张内存位图上,然后在一次性的贴位图,他可以提高绘图速度,也能避免闪烁。
DoubleBuffer=true是C#窗体的属性,设置了此属性估计系统本身会起用无效区的内存位图缓存,而不需要程序员Bitmap处理。
这里对双缓存的通常做法不作介绍,网上的相关资料很多。
说实话,我对使用双缓存能提升绘图效率表示怀疑,理由很简单,同是DC,同是绘制1000条线段,有什么理由内存DC就比窗口DC快(当然这个我没有作具体的测试,这个有空可以测试下)。
我还稍微怀疑使用双缓存绘图比直接使用窗口DC绘图还慢一些,理由有二:一是使用双缓存需要增加创建内存DC和内存位图的操作;二是使用双缓存还需要增加一个把内存DC拷贝到窗口DC的操作。
那么双缓存的主要作用是什么?其实就是解决绘图过程的闪烁问题,改善绘图效果。
二.Windows环境下二维绘图引擎的选取和绘图效率的一个重要相关因素是绘图引擎。
Windows环境下二维绘图引擎有多种选择:GDI、GDI+、DirectDraw、QT、Agg、Cairo、skia、Direct2D 、Direct3D、OpenGL等。
下面我逐一作一个简单的分析:GDI:微软原生的二维绘图引擎。
优点:微软的全力支持,作为操作系统核心层效率方面不用担心,支持多种开发框架(含语言):Win SDK、MFC、Delphi等。
缺点:基于过程,缺乏面向对象,使用起来不太方便,不支持反锯齿,不支持复杂的绘图效果(这个相对于GDI+而言)。
GDI+:微软后来推出的二维绘图引擎。
c语言的7种数据类型C语言是一种广泛使用的编程语言,在C语言中有7种基本的数据类型,分别为:整型、浮点型、字符型、布尔型、指针类型、数组类型和结构体类型。
下面将对每种数据类型进行详细介绍。
1. 整型(int):整型用于表示整数,它可以分为有符号整型和无符号整型。
有符号整型可以表示正数、负数和零,而无符号整型只能表示非负数。
在32位操作系统下,有符号整型的取值范围为-2^31到2^31-1,而无符号整型的取值范围为0到2^32-1。
2. 浮点型(float和double):浮点型用于表示带有小数部分的数字。
C语言提供了两种浮点类型:float和double。
float类型在内存中占用4个字节,而double类型在内存中占用8个字节。
它们的取值范围和精度不同,double类型的取值范围和精度比float类型更大。
3. 字符型(char):字符型用于表示单个字符,它是基本的文本数据类型。
char类型在内存中占用1个字节,可以表示256个不同的字符。
在C 语言中,字符型变量使用单引号来表示,如:'A','0'等。
4. 布尔型(bool):布尔型用于表示真或假的值。
在C语言中,布尔类型是通过预处理指令#define来定义的,可以使用true和false关键字来表示真和假。
5. 指针类型(pointer):指针类型用于存储变量的内存地址。
在C语言中,使用指针可以实现对内存的直接访问和操作。
指针类型在内存中占用4个字节或8个字节,取决于所在的操作系统。
6. 数组类型(array):数组类型用于存储一系列具有相同数据类型的元素。
在C语言中,数组可以是一维的,也可以是多维的。
数组的长度在定义时就需要确定,并且无法改变。
7. 结构体类型(struct):结构体类型用于自定义复杂的数据类型。
结构体是一种可以包含多个不同数据类型的数据类型,它可以将不同的数据组合在一起,形成一个逻辑上的整体。
本文主要介绍了directfbrc文件的使用及参数的详细说明,在directfb应用程序启动做初始化阶段,都会去试图读取该文件。
摘要:本文主要介绍了directfbrc文件的使用及参数的详细说明,在directfb应用程序启动做初始化阶段,都会去试图读取该文件。
---------------------------------------------------------------------------------------------------------------------声明:此文为原创,欢迎转载,转载请保留如下信息作者:聂飞(afreez)联系方式:afreez@ (欢迎与作者交流)初次发布时间:2006-06-06不经本人同意,不得用语商业或赢利性质目的,否则,作者有权追究相关责任!-----------------------------------------------------------------------------directfbrc是DirectFB的配置文件。
它被所有的DirectFB应用程序在启动时读取,有两个这样的文件,一个是存放在/etc/direcfbrc,是个全局的,另一个是存放在$HOME/.directfbrc,它是个局部的,可以覆盖系统的设置。
需要注意的是,这两个文件都不是默认存在的,是需要你自己建立的,不要象我一样,刚开始的时候到处找也没有找到,呵呵。
在directfbrc使用的参数也可以在命令行里传递给DirectFB应用程序,只需要加上前缀:--dfb:相关语法:directfbrc文件每一行包含一个变量。
注释行以井号“#”开始,一直到行尾。
空行被忽略。
许多参数只是一种开关,控制着一些特性的开/关。
这些开关选项有一个no-变量,可以关闭相应的特性。
下面介绍一些实用的参数和一些默认的参数。
参数:以下参数可以在directfbrc文件中设定system=<system>设定使用的图形系统。
dma_mmap_coherent函数-回复dma_mmap_coherent函数是Linux内核中的一个函数,用于映射一块连续的DMA内存区域。
在本文中,我们将详细介绍dma_mmap_coherent函数的作用、实现原理以及使用方法。
首先,让我们了解一下DMA(Direct Memory Access)的概念。
DMA 是一种计算机系统中的技术,它允许外设直接访问内存,而不需要CPU 的干预。
这样可以提高数据传输的效率,减少CPU的负载。
在许多嵌入式系统和高性能计算领域,DMA被广泛应用于数据传输和外设控制。
在Linux内核中,dma_mmap_coherent函数用于将一块DMA内存区域映射到用户空间。
通常情况下,内核分配给DMA设备的内存是物理上连续的,并且可以通过物理地址访问。
然而,由于用户空间只能访问虚拟地址,因此需要通过映射将物理地址转换为虚拟地址,以便用户空间可以直接访问DMA内存。
那么,dma_mmap_coherent函数是如何实现这个映射的呢?让我们来分析一下其实现原理。
dma_mmap_coherent函数的实现主要涉及到三个关键的数据结构:vm_area_struct、struct page和dma_mapping_ops。
首先,当用户空间调用mmap系统调用将DMA内存映射到用户空间时,内核会创建一个vm_area_struct结构体来表示这个映射关系。
vm_area_struct结构体中保存了映射的起始地址、长度、权限等信息。
然后,dma_mmap_coherent函数会根据vm_area_struct中的信息,遍历这块DMA内存区域中的所有页面。
对于每个页面,它会通过struct page结构体来查找相应的物理页面。
struct page结构体是Linux内核中用于描述物理页面的数据结构。
通过物理地址可以找到对应的struct page结构体,从而获得页面的信息,例如页面的引用数、页面的标志等。
关于内存DC的使用(一)在windows下搞图形界面的设计难免要使用到内存DC,将所有的绘制工作先绘制在内存DC上,然活一次性拷贝到屏幕DC上。
可消除一些图形的闪烁问题,当然还有其他的用处,比如简单游戏中的象素碰撞检测等等。
1,创建兼容DCCDC m_MenDC;CDC m_MenDC2;CDC m_MenDCMap;CDC m_MenDCMask;//这个是一个要创建的兼容位图CBitmap m_Bitmap1;m_MenDC.CreateCompatibleDC(GetDC());m_MenDCMap.CreateCompatibleDC(GetDC());m_MenDCMask.CreateCompatibleDC(GetDC());m_MenDC2.CreateCompatibleDC(GetDC());m_Bitmap1.CreateCompatibleBitmap(GetDC(),1024,768);2,为兼容DC选入一张位图,或兼容位图。
m_MenDC.SelectObject(m_hbmpBK);m_MenDC2.SelectObject(m_Bitmap1);3,好了,现在可以在兼容DC上做绘制工作了,m_btnReturn.DrawButton(m_MenDC); //绘制按钮m_btnUp.DrawButton(m_MenDC);m_btnLeft.DrawButton(m_MenDC);m_btnRight.DrawButton(m_MenDC);m_btnDown.DrawButton(m_MenDC);m_btnBack.DrawButton(m_MenDC);m_btnAnew.DrawButton(m_MenDC);m_btnFull.DrawButton(m_MenDC);m_MenDC2.BitBlt(0,0,1024,768,&m_MenDC,0,0,SRCCOPY);// 绘制背景 DrawPath(&m_MenDCMap); //绘制路径if(m_bFullView){SetStretchBltMode(m_MenDC2.GetSafeHdc(),HALFTONE );m_MenDC2.StretchBlt(m_rtMap.left,m_rtMap.top,m_rtMap.Width(),m_r tM ap.Height(), &m_MenDCMap,0,0,m_iMapWidth,m_iMapHeight,SRCCOPY); //绘制平面图,查看全图}else{m_MenDC2.BitBlt(m_rtMap.left,m_rtMap.top,m_rtMap.Width(),m_rtMap.He ight(), &m_MenDCMap,m_xOffset,m_yOffset,SRCCOPY); //绘制平面图}4,绘制结果的显示,将这些东西拷到屏幕DC上,所谓的双缓冲就是把所有的绘制工作都做在一个内存DC上。
// 最后一次拷到屏幕DC上,只能有一次dc.BitBlt(0,0,1024,768,&m_MenDC2,0,0,SRCCOPY); 这里我所强调的“一次”;是不要同时将几个DC的内容都拷到屏幕DC上,这样没有起到双缓冲的效果。
如果你搞了很多个内存DC,想把这些东西都显示出来,那你应该先把这多个内存DC的内容同时拷到另外一个内存DC上,再把这个内存DC的内容拷到屏幕DC上。
5,注意的一点:m_hbmpBK=HBITMAP)::LoadImageAfxGetInstanceHandle),path+"Bk4.bmp",IMAG E_BITMAP,0,0,LR_LOADFROMFILE);m_MenDC.SelectObject(m_hbmpBK);m_MenDC.绘制工作。
在内存DC上所做的绘制工作实际上都是绘制在你选入的那张位图m_hbmpBK 上。
就是说m_hbmpBK 句柄所指向的位图内容改变了,如果以后你在其他的地方在用这个m_hbmpBK 的时候,你会发现这已经不是你想象中的Bk4.bmp了,^_^。
如果想再用原图,必须的重新从硬盘导入。
还要注意的一点是,一个位图句柄不可同时选入到两个内存DC中。
内存DC对于选入的位图具有排他性,如果你m_MenDC.SelectObject(m_hbmpBK);而m_hbmpBK 已经被其他的内存DC选用了话,那刚才这句m_MenDC.SelectObject(m_hbmpBK);是毫无意义的。
在网上看到了许多的关于装位图装载到离屏表面的文章,但是都是使用了WIN32函数,虽然有效,但不是很通用。
如果我们要装载其它的格式的文件使用不了WIN32函数,不就无能为力了吗?于是我想直接操作文件,直接读取位图文件的数据到离屏表面。
网上还是有这样的文章的,但是很少,并且没有过多的说明。
其实,装载文件到离屏表面也很简单,不需要什么算法知识就可以完成的。
主要的方法是(以256色位图为准):1、将位图文件的颜色表和图像数据读入内存。
2、创建前后表面,离屏表面。
3、根据颜色表创建调色板。
4、将位图数据传输到离屏表面。
5、利用后表面的BLTFAST将图像输出到后表面。
6、翻转表面,将图像显示到屏幕。
先声明几个变量:BITMAPFILEHEADER bmfh; //位图文件头BITMAPINFOHEADER bmih; //位图信息头RGBQUAD rgb[256]; //颜色表首先,我了解了一下位图文件的结构,其实也是很简单的。
1、文件头: typedef struct tagBITMAPFILEHEADER{UINT bfType; //文件标志DWORD bfSize; //文件大小UINT bfReserved1,bfReserved2;DWORD bfOffBits; //数据偏移}BITMAPFILEHEADER;bfType:是位图的文件标志,为"BM"。
当你从文件中读出放到一个变量中时,它是"MB",即0x4d42。
在内存中,存放是高位在前的。
所以在磁盘上是"BM",读到内存就为"MB"了,不信?你一个字节一个字节的读,将这两个字节存到两个字符型变量char ch1,ch2;中,你会发现ch1=='B',ch2=='M'。
bfSize:位图文件的大小。
等于位图文件头+信息头+颜色表+位数据。
以字节为单位即:sizeof(bmfh)+sizeof(bmih)+sizeof(RGBQUAD)*256+bmih.biSizeImagebfOffBits:位图数据偏移.如果你想直接读取位图的数据.使用fseek(fil_ptr,bmfh.bfOffBits,SEEK_SET); 这样,可以直接将文件指针指向位图数据开始的地方。
2、信息头: typedef struct tagBITMAPINFOHEADER{DWORD biSize; //信息头大小。
40字节LONG biWidth,biHeight; //位图实际宽、高度。
WORD biPlanes; //WORD biBitCount; //位图每像素的位数。
DWORD biCompression; //DWORD biSizeImage; //位数据的大小(字节)LONG biXPelsPerMeter,biYPelsPerMeter; //DWORD biClrUsed; //DWORD biClrImprotant; //}BITMAPINFOHEADER;介绍一些重要的部分:biSize:信息头大小,即此结构的大小。
为40字节。
即sizeof(bmih)。
biWidth,biHeight:位图的像素宽、高。
biBitCount:位图每像素的位数。
指定了这个位图文件的颜色深度。
在这个例子中是8。
biSizeImage:位图数据区的大小。
这里有需要注意的:位图数据每行是以4字节增充的,如果是一个256色的位图。
它的像素占一字节。
如果图像宽度为80像素,则图像每行为80字节,如果图像宽度为79像素,则磁盘上的位图文件仍然是80字节。
(78,77像素每行也为80字节)。
图像每行76,75,74,73像素,则它在文件中占76个字节。
因此在从磁盘读出数据时要知道每行的字节数,这里我使用 bytperlin=bmih.biSizeImage/bmih.biHeight; 从磁盘的位图文件中读取数据。
首先是位图文件头,信息头。
使用如下语句便可:fread(&bmfh,sizeof(bmfh),1,fil_ptr);fread(&bmih,sizeof(bmih) ,1,fil_ptr);这时两个结构bmfh,bmih就存放了我们需要的一些关于位图的信息了。
再读入颜色表:RGBQUAD* prgb; prgb=(RGBQUAD*)malloc(sizeof(RGBQUAD)*bmih.biBitCount); //注意,在使用指针之前一定要给它分配内存空间,否则的话程序会退出。
fread(prgb,sizeof(RGBQUAD),1<<BMIH.BIBITCOUNT,FIL_PTR); 这样,我们需要的颜色值放入了动态分配的内存空间了。
但是在DD程序中设置调色板的话需要的是PALETTEENTRY结构数组,因此我们要得到PALETTEENTRY结构数据的值。
PALETTEENTRY*ppal= (PALETTEENTRY*)malloc(sizeof(PALETTEENTRY)*bmih.biBitCount);for(int i=0;i<(1<<BMIH.BIBITCOUNT);I++){ppal[i].peRed=prgb[i].rgbRed;ppal[i].peGreen=prgb[i].rgbGreen;ppal[i].peBlue=prgb[i].rgbBlue;ppal[i].peFlag=0;} 之后我们就可以通过这个结构来取得LPDIRECTDRAWPALETTE接口指针。
之后为前表面设置调色板(在后面的程序中)。
最后是读入位图位数据了,每个像素是1个字节。
位图图像数据的大小在信息头中已经给定了:bmih.biSizeImage 这个大小包括实际的图像数据和为了每行达到4字节而扩充的字节数。
我们用如下方法读入: BYTE*pbuffer=(BYTE*)malloc(sizeof(BYTE)*bmih.biSizeImage); //注意,一定要分配内存空间,否则程序会退出fread(pbuffer,sizeof(BYTE),bmih.biSizeImage,fil_ptr); 这样,位图中的数据我们全部读入了。