DIB(设备无关位图)是存储在磁盘上的位图文件,可以从磁盘读到内存中或从内存保存到磁盘上,它的磁盘文件结构是标准化的,在Linux、Unix及Windows上都可以以同样效果显示。位图是最接近硬件的图像格式,Windows显示的核心是位图,它的SDK API专门提供了一组用于操作DIB文件的函数。但是由于这样或那样的原因,高效合理的使用这些DIB API是需要了解不少历史和使用背景的,在这里我抽茧剥丝介绍和演示DIB的使用,相信对你更好的使用DIB文件有帮助,由于DIB函数比较多,这里分为三部分介绍,首先是DIB的读入、保存和显示。

1.DIB文件的组成

关于DIB文件的组成很多地方有详细描述,这里不再详细赘述。主要分为如下几个部分:

  • 文件表头
  • 信息表头
  • RGB色彩对照表(不一定有)
  • 位图像素位

其中

  1. 文件表头包含了文件类型(BMP)、文件大小及文件中实际像素数据保存的偏移值
  2. 信息头包含了位图的大小、位面数、位深度、压缩及掩码等信息
  3. RGB色彩对照表也称调色板,8位以下位图可能包含有调色板信息,16位及以上一般没有调色板
  4. 位图像素位为实际位图数据保存区

其中,信息表头和RGB调色板合称为位图信息。

2.Windows DIB内存数据结构

我们要把DIB数据读入到内存中,那么就要分配相应的内存,把读入的数据写到对应的内存区中,这里SDK 提供的数据结构是各种结构体,结构体的各个字段对应磁盘文件中各个信息值。我们这里为了逻辑清楚,使用最常使用的DIB结构体。

如图,文件中的数据读到对应的内存结构中完成文件的读入,内存结构中的数据写到对应的文件中完成文件的保存。

其中

  1. 文件信息头数据读到BITMAPFILEHEADER结构体中
  2. 位图信息头读到BITMAPINFOHEADER结构体中
  3. 位图调色板读到RGBQUAD结构体数组中
  4. 位图数据读到根据位图信息头提供的信息而分配的相应大小的数据区中

把BITMAPINFOHEADER和RGBQUAD[0]作为BITMAPINFO结构体成员,这样一方面是为了和磁盘文件对应,另一方面也是为了访问调色板数据方便。

这里的磁盘文件中各个段在磁盘上保存位置必须是连续的,但是对应的内存中文件信息头、位图信息和位图数据三大块不一定要是连续的。因此,在读入文件中你既可以一次性读入磁盘文件到连续的内存中,也可以分开读入到三个分别连续的内存中,后面会做相关演示。

3.DIB显示

前面讲了读入DIB到对应的内存结构中,现在我们要怎么把对应的内存结构中的像素数据显示出来呢?
如下图,为内存中指定点的像素值是怎么样一步步显示到显示器上指定位置的,这里做了一些简化,没有考虑压缩位图和显示器的显示精度
这个图看起来很复杂,实际上在这里我也没想把它完全讲出来,如果你之前有一定的DIB使用经验,那么这幅图可以帮你更好的理解整个显示过程,如果没有DIB使用经验,那么不用仔细看这幅位图。从这幅图,我们知道显示的时候需要的数据是:内存中位图数据、内存中位图信息、内存中位图指定要显示区域、显示器上用于显示的区域。
提供了需要的信息后,Windows提供了SetDIBitsToDevice和StretchDIBits函数帮我们完成这一显示过程。
两个函数的原型分别如下
int SetDIBitsToDevice(  HDC hdc,                 // handle to DCint XDest,               // x-coord of destination upper-left cornerint YDest,               // y-coord of destination upper-left corner DWORD dwWidth,           // source rectangle widthDWORD dwHeight,          // source rectangle heightint XSrc,                // x-coord of source lower-left cornerint YSrc,                // y-coord of source lower-left cornerUINT uStartScan,         // first scan line in arrayUINT cScanLines,         // number of scan linesCONST VOID *lpvBits,     // array of DIB bitsCONST BITMAPINFO *lpbmi, // bitmap informationUINT fuColorUse          // RGB or palette indexes);
int StretchDIBits(HDC hdc,                      // handle to DCint XDest,                    // x-coord of destination upper-left cornerint YDest,                    // y-coord of destination upper-left cornerint nDestWidth,               // width of destination rectangleint nDestHeight,              // height of destination rectangleint XSrc,                     // x-coord of source upper-left cornerint YSrc,                     // y-coord of source upper-left cornerint nSrcWidth,                // width of source rectangleint nSrcHeight,               // height of source rectangleCONST VOID *lpBits,           // bitmap bitsCONST BITMAPINFO *lpBitsInfo, // bitmap dataUINT iUsage,                  // usage optionsDWORD dwRop                   // raster operation code);

可以看到两个函数的从上到下依次需要提供的信息为:待显示窗口的DC、待显示窗口的用于显示的区域、位图中要显示部分、位图数据、位图信息、其它选项,具体各个参数含义参见MSDN。

4.代码演示

这里的演示程序界面如下
在读入中可以选择一次性读入还是分片段读入,两种方式的区别在于读入以后在内存中位图各个段是否连续。保存也对应读入的两种方式。显示可以选择正常居中显示还是拉伸到整个窗口屏幕显示,前者对应SetDIBitsToDevice函数,后者对应StretchDIBits函数。这里只提供一次性读入、保存和显示代码,完整代码参见附件。
BOOL DibTotalLoad(PTSTR szBmpFile, PBITMAPFILEHEADER *ppbmfh,PBITMAPINFO *ppbmi,PBYTE *ppBits,PLONG pBmpWidth,PLONG pBmpHeight)
{HANDLE             hFile;DWORD             dwFileSize, dwBytesRead;BOOL                bSuccess;//打开文件hFile = CreateFile( szBmpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);if (INVALID_HANDLE_VALUE == hFile){return FALSE;}//判断BMP文件大小(所有部分)dwFileSize = GetFileSize(hFile, NULL);//分配对应大小内存用于保存磁盘BMP文件内容*ppbmfh = malloc(dwFileSize);if (!(*ppbmfh)){CloseHandle(hFile);return FALSE;}//读入文件内容bSuccess = ReadFile(hFile, *ppbmfh, dwFileSize, &dwBytesRead, NULL);CloseHandle(hFile);//校验读入是否正确和文件是否为BMP文件if (!bSuccess ||(dwBytesRead != dwFileSize) ||(*ppbmfh)->bfType != *(WORD *)"BM"){free(*ppbmfh);return FALSE;}//计算剩余的返回参数*ppbmi = (PBITMAPINFO)(*ppbmfh+1);*ppBits = (PBYTE)(*ppbmfh) + (*ppbmfh)->bfOffBits;*pBmpWidth = (*ppbmi)->bmiHeader.biWidth;*pBmpHeight = (*ppbmi)->bmiHeader.biHeight;return TRUE;
}BOOL DibTotalSave(PTSTR szBmpFile, PBITMAPFILEHEADER pbmfh)
{BOOL       bSuccess;DWORD      dwBytesWrite;HANDLE     hFile;//打开要写入的文件hFile = CreateFile(szBmpFile,GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (INVALID_HANDLE_VALUE == hFile){return FALSE;}//写入文件bSuccess = WriteFile(hFile, pbmfh, pbmfh->bfSize, &dwBytesWrite, NULL);CloseHandle(hFile);if (!bSuccess || dwBytesWrite != pbmfh->bfSize){DeleteFile(hFile);return FALSE;}return TRUE;
}void ShowDib(HDC hdc, PBITMAPINFO pbmi, PBYTE pBits, long nBmpWidth, long nBmpHeight,long cxClient, long cyClient,BOOL bFull)
{if (FALSE == bFull)//居中显示{SetDIBitsToDevice( hdc,(cxClient-nBmpWidth)/2, (cyClient-nBmpHeight)/2,nBmpWidth, nBmpHeight,0, 0,0, nBmpHeight, pBits, pbmi,DIB_RGB_COLORS);}else//拉伸显示{SetStretchBltMode(hdc, COLORONCOLOR);StretchDIBits(hdc, 0, 0,cxClient, cyClient,0, 0,nBmpWidth, nBmpHeight, pBits, pbmi, DIB_RGB_COLORS, SRCCOPY);}
}

这里需要补充说明的是,为了保证逻辑清晰,本文没有考虑位图的OS/2兼容格式和自顶向下位图等特殊情况,错误处理可能也不是特别完善(没有考虑超大位图的情况),演示的代码在大部分情况下是适用的,只是为了起抛砖引玉的作用。关于DIB的详细描述当推Petzold的《Windows 程序设计》的“与设备无关的位图”一章,这一章描述非常详细,但是个别地方有些晦涩,结合本文来看可以加深理解。

完整源代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219

Windows DIB文件操作详解-1.DIB的读入、保存和显示相关推荐

  1. Windows DIB文件操作详解-2.DIB转DDB

    上一节讲到显示DIB的显示时需要将文件内容读到内存中(如下图),当使用SetDIBitsToDevice和StretchDIBits函数显示时中间涉及到一系列的转换,这样大大增加了性能消耗,特别是在重 ...

  2. Windows DIB文件操作详解-4.使用DIB Section

    前面讲了为了提高DIB的显示性能和效率,我们将DIB转换成DDB,但是这又遇到一个问题,如果我想操作DIB的数据的话,显然是不能使用DDB:一是因为DIB转DDB时发生了颜色转换,再就是DDB无法直接 ...

  3. Windows DIB文件操作详解-3.DDB转DIB

    上一节讲到DIB转换成DDB以提高位图显示性能,那么这里自然想到DDB转DIB.DDB转DIB有什么用呢?最常见的应用场合就是你得到了一个屏幕截图的DDB,现在想把它保存到磁盘上,这时候就要用到DDB ...

  4. C 和 C++ 文件操作详解

    来源:http://www.cnblogs.com/likebeta/archive/2012/06/16/2551662.html 来源:http://www.cnblogs.com/likebet ...

  5. Qt QFile文件操作详解

    Qt QFile文件操作详解 很多应用程序都需要具备操作文件的能力,包括对文件内容进行读/写.创建和删除文件等,甚至某些应用程序的诞生纯粹是为了操作文件,比如 WPS Office.PDFedit 等 ...

  6. Python文件操作详解(一)

    今天继续给大家介绍Python相关知识,本文主要内容是Python文件操作详解. 一.Python文件处理简介 使用Python可以编写程序处理文件,Python可以处理的文件值得是任何存储在辅助存储 ...

  7. C++文件操作详解(ifstream、ofstream、fstream)【笔记本】

    C++文件操作详解(ifstream.ofstream.fstream) C++ 通过以下几个类支持文件的输入输出: ofstream: 写操作(输出)的文件类 (由ostream引申而来) ifst ...

  8. 简述python文件操作的流程_Python文件操作详解

    这篇文章主要介绍了Python 文件操作的详解及实例的相关资料,希望通过本文大家能够理解掌握Python 文件操作的知识,需要的朋友可以参考下 Python 文件操作的详解及实例 一.文件操作 1.对 ...

  9. 【Kubernetes 018】cfssl创建证书并结合RBAC的RoleBinding配置新用户config文件操作详解

    安全性是企业生产环境中的头等大事,对于访问同一集群的不同用户或者用户组来说,将权限分级是很有必要的.和很多云厂商一样,k8s也是采用按照角色和用户绑定的方式来分配权限的,这一节我们就来实际操作下,新建 ...

最新文章

  1. LMDB中的mmap、Copy On Write、MVCC深入理解——讲得非常好,常来看看!
  2. gulp.js 的安装以及使用
  3. [Swift]LeetCode835. 图像重叠 | Image Overlap
  4. 第三章:3.栈和队列 -- 栈与递归的实现
  5. 医学生如何选专业选科室?全网最全最详细分析
  6. wordpress php7 mysql_WordPress可以使用PHP7的MySQLi扩展
  7. 秋风到,ModelArts“ AI市场算法Fast-SCNN指南”秋膘贴起来
  8. 标识符的作用域与可见性
  9. verilog之门级相关知识
  10. ShortcutMapper 是应用程序的键盘快捷键
  11. python 多行缩进_【python cookbook】改变多行文本字符串的缩进
  12. 基于Java、JSP的会议室预约系统毕业设计
  13. ModBus那些傻傻分不清
  14. 离散数学 --- 命题逻辑 --- 基本推理形式和自然演绎法推理
  15. Java文件操作——简单文件搜索
  16. Solaris adsl上网
  17. Linux环境下Python操作word
  18. python采集原神高清pv图和语音
  19. goland debug高级技巧
  20. java根据url获取pdf流_从URL获取动态创建的PDF

热门文章

  1. IntelliJ IDEA 2016.1(64) : Unsupported java version Cannot start under Java 1.7.0_79-b15: Java 1.8 o
  2. wifi驱动的理解(1)——驱动架构
  3. 个人所得税计算C语言实现
  4. 还记得当年的c-free和Borland c++5.5编译器吗?
  5. 如何正确运用计算机,如何使用电脑?使用电脑正确方法
  6. 收纳箱底部滚轮怎样装_教你抽屉滑轨怎么安装【图】
  7. can‘t open/read file: check file path/integrity
  8. 会话初始协议SIP与SDP简介
  9. HTML用css把英文字母改大,css font-variant全大写并缩小英文字母字体
  10. 项目经理对项目各阶段需求的把控力度