如何显示一幅位图(位图的来龙去脉--续)

在这里我就不分析结构体成员的具体意思了,应该学会查看帮助手册。了解BMP文件的格式后,还需要了解在屏幕上显示位图需要什么样的数据结构。

这里简要描述下:WINDOWS中位图有两种格式:DDBDIB DDB由我们熟悉的BITMAP结构和图像数据构成,MFC有支持DDBCbitmap类,因此显示DDB位图既可以用SDK的函数也可用MFC类。DIB的结构和上面说的BMP位图结构很接近,具体来讲,DIB结构 = BMP结构 – BITMAPFILEHEADER。显示DIB的函数是SetDIBitsToDevice

这里我想说下SetDIBitsToDevice倒数第二个参数,它是一个BITMAPINFO结构的指针,它等于信息头和调色板的入口地址。最后说几点程序。

bfType表示文件类型,必须是“BM”,占两个字节,正好一个字符一个,他和这里的19778什么关系呢?自然想到的是字符的ASCII值,‘B’和‘M’的ASCII分别为:6677,都是十进制,转换为0x420x4D19778的十六进制是0x4D42 。怎么感觉是反着来着的呢?哈哈,被你说中了,WORD = 16bit 两个字节,存放数据是先存低字节,后存高字节,因此字符‘B’放在低字节,字符‘M’存在高字节,最终bfType = 0x4D42。明白了吗?但是这里的“BM”有什么用呢?一般是用于判断文件是否是一个BMP文件。可以这样判断:

 

似乎你对这个判断还不是很信服,你会觉得那别人也写一个结构体,在这个结构体里面赋bfType = 19778。岂不也可以蒙混过关。不行的,你还可以通过比较bfSize值和文件实际大小,因为bfSize是文件的大小,若两者不等,说明后面的数据(这里的BITMAPFILEHEADER是头嘛)部分有损坏或者干脆文件不合法,你觉得我的话有道理吗?这里我暂时没找到如何得到用fopen打开的文件的大小,因此我省去了判断文件是否损坏的代码。

BITMAPFILEHEADER 大小为14Bytes

 

BITMAPINFOHEADER结构体大小为40Bytes。它的第一个成员就表示本结构体的大小,调试值也是40 。下面两个参数是位图图像的宽度和高度,单位为像素,和实际的也吻合(屁话,肯定吻合的,这里我只是跟踪下加深理解)biPlanes是啥呢?帮助手册说是“This value must be set to 1呵呵,biBitCount表示每个像素点的颜色的位数,这里为24表示真彩色图,这里我还想不厌其烦的强调一下:24位可以表示2^24种颜色,但是一个实际图片不一定囊括了所有这2^24种,有可能只有其中的1000种。实际使用多少种颜色可以通过biClrUsed知道(这名字起的,顾名思义啊,呵呵),这个参数指定位图实际使用的颜色的个数,是调色板中颜色索引的个数(呵呵,照MSDN翻译来的,总觉得没翻译正确,原文是:Specifies the number of color indexes in the color table that are actually used by the bitmap

接下来我发现了一个问题,这个问题便是位图的大小计算。按照上面的计算方法,这幅图片尺寸为182*267,没有使用调色板,24位真彩色,因此大小=182*267*3Bytes = 145782Bytes。但是这里的biSizeImage却是146316Bytes。更有甚者,前面那个结构体BITMAPFILEHEADER bfSize成员也是文件的大小。我们调试下,看看它的值是146370 。这三个值,到底哪个才是真正占用计算机磁盘的大小呢?你这里犯了一个小小的错误,这里的biSizeImage不是指文件的大小,它是指图像的大小,即真正图像数据的字节数,因此它应该等于bfSize-bfOffBits = 146370 – 54 = 146316,吻合了吧(其实这里的54我们可以知道是怎么得到的,24真彩色没有调色板,因此从文件头开始多少字节是图像数据的起始=两个结构体的长度之和=14+40)。问题还没结束,为什么“182*267*3Bytes = 145782Bytes”计算值不对呢(正确值不用说应该是146370)?哈哈,你这里又犯了一个小小的小错误,我们前面这么算只是一种估算,当然不知者谈不上错误,图像的高度乘以宽度乘以每像素的字节数不一定是实际图像的大小,因为在BMP文件格式中规定:每行的字节数必须是4的整数倍,不是4的整数倍要补零。182*267 ,它的宽度是182,每行的字节数182*3Bytes 不是4的整数倍,补零,补多少呢?182*3 = 546,因此需要补2个字节的0,这样整个图片需要增加2*267 = 534字节,最终大小 = 182*267*3Bytes + 534Bytes = 146316

 

到现在为止,位图结构的两个大结构体已经读出来了,下来该干什么呢?我觉得现在应该看看SetDIBitsToDevice参数了,参数需要什么,我们就构造什么,对吧,怎么构造,肯定是和我们刚刚读出来的两个结构体有关喽。

 

我们先看看倒数第二个和第三个参数怎么赋值吧。

倒数第三个参数是DIB数据(今天徐老师和我说的“裸数据”就是它),到目前为止,代码已经读完信息头,对于24真彩色,下面继续读的数据即为“裸数据”,因此还需要定义一个变量用于存放这个裸数据,定义什么样子的变量呢?首先一定是指针,因为裸数据的大小为biSizeImage ,它是BYTE 类型,因此这样定义:BYTE * pNakeData; 好了,现在可以为之分配空间了,分配biSizeImage BYTES大小的空间。用什么函数呢?newmalloc?不要奇怪,对于一个像我这样的新手,这些问题就不能回避,是随便一个函数都行还是有特殊的函数,这些都需要讨论。于是乎我百度看看:

天啊,以前懵懵懂懂的知道栈内存,堆内存,如果我问你,你对它们了解多少?为什么需要堆内存?这些内存是物理内存吗?我把自己的理解再次整理下:我们在程序中定义一个变量,我们其实已经申请了内存,只是这块内存的生命由代码决定(或者叫作用域),生命到了,这个内存就被释放了,这样的内存起个名字就是“栈内存”,如果我想申请一个内存,要求它生命周期能长一点,且,它的生命由我(程序员)决定,这样的内存也有,并且它的名字叫“堆内存”,正因为这种特殊性,它的诞生由程序员决定(实际函数就是new 等等),它的释放也必须由程序员完成,否则就会造成可怕的“内存泄露”,(呵呵,不用怕,不是毒气泄露,不会把你怎地,只会让你的计算机崩溃)。其实这里还有一种内存没有讲,它就是静态内存分配,它在计算机启动的时候就已经分配好了。

这里想说个蛮有意思的话题,你想过“堆内存”有多大吗?嘿嘿,你可以按这个方法试试,你不停地new ,知道new 失败了,堆内存估计就被你用完了。(这个是网上一个人说的,我觉得行不通,因为当物理内存消耗殆尽的时候,操作系统会使用虚拟内存,你的电脑会很慢,不知道会出现什么样子呢。呵呵。)

网上一片博客:http://blog.csdn.net/cszddd/archive/2009/04/13/4069748.aspx

呀,分配内存的函数太多了:new malloc HeapAlloc VirtualAlloc GlobalAlloc LocalAlloc

以下是网上找来的:

HeapAlloc();这个是windowsAPI函数,分配虚拟内存,不一定就分配物理内存,看后面参数而定。

new   c++的分配内存操作符,它内部是调用malloc的,mallocC用来分配内存的。     windows平台,   malloc又最终是调用HeapAlloc这个API来分配堆内存  new   调用   malloc,   malloc最终调用HeapAlloc     delete调用free(注意delete还多一步是调用析构函数),   free最终是用HeapFree

再自己总结一下  
  operator   new
allocHeapAlloc都是在堆中分配内存,跟栈没有关系,new     alloc分配内存的方式应该是一样的,所以用alloc分配的空间可以用delete释放,而用new分配的空间也可以用free释放(我测试过),不同的是,用delete在释放内存前还会调用析购函数,所以用new创建的最好用delete释放。另外,HeapAllocHeapFree必须搭配使用,因为HeapAlloc分配的内存空间,deletefree不能正确释放,newmalloc分配的空间也不能被HeapFree正确处理

To free a block of memory allocated by HeapAlloc, use the HeapFree function

这个是讲分配效率的文章:http://blog.csdn.net/wuhuiran/archive/2008/11/30/3414742.aspx

 

倒数第二个参数是一个BITMAPINFO结构体指针,我已经在程序中定义了全局变量:

BITMAPINFO结构体

 

不说了,需要代码的朋友可以与我联系!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值