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

前面讲了为了提高DIB的显示性能和效率,我们将DIB转换成DDB,但是这又遇到一个问题,如果我想操作DIB的数据的话,显然是不能使用DDB:一是因为DIB转DDB时发生了颜色转换,再就是DDB无法直接提取指定像素点的数据。那么我们怎么办呢,Windows使用一种折中的方式来达到这一目标(既提高了显示效率和性能,又可以直接操作像素点)。

1.DIB Section存储和显示

Windows使用DIB块(DIB Section)来存储DIB数据,其内存结构示意图如下

其实,和我们自己读入DIB数据到自己分配的各个数据区感觉是一样的,只是现在这些DIB的各个数据区是由Windows自己分配维护的,值得注意的是这些Windows自己维护的DIB数据区是通过HBITMAP句柄来组织的,这个HBITMAP句柄和BITMAP的HBITMAP句柄是不一样的,至于为什么同一种句柄可表示不同的对象可以查看笔者的博文“深入了解Windows句柄到底是什么”。

继续说现在的问题,这里存储的是DIB的数据,只有在使用BitBlt和StretchBlt显示的时候,才会发生DIB->DDB的转换,显然这里BitBlt和StretchBlt会对句柄属性做一个判断来确认指向BITMAP的HBITMAP及指向DIB Section的HBITMAP的不同操作。当然,这里的DIB Section各个区不一定是连续的,这是一定要注意的。

这里Windows自己维护DIB各个数据区,做了一定优化,所以比我们自己分配和存储DIB各个数据区的显示效率和性能要高。

2.DIB Section的相关函数使用

明白了Windows对于DIB Section的存储原理和转换规则以后,我们说一下DIB Section的相关函数使用。

1.CreateDIBSection

HBITMAP CreateDIBSection(
  HDC hdc,          		// 设备描述表句柄
  CONST BITMAPINFO *pbmi,	// 包含Info Header、Mask、Color Table数据的BITMAPINFO指针
  UINT iUsage,      		// DIB_PAL_COLORS或DIB_RGB_COLORS
  VOID *ppvBits,    		// 指向存储位图数据的地址的指针
  HANDLE hSection,
  DWORD dwOffset
);

使用如下:

1.一般不考虑后两个参数

2.传入包含DIB位图信息头(Info Header)、压缩掩码(Mask)及调色板信息(Color Table,主要针对位图深度<=8)的BITMAPINFO* pbmi指针,这三者必须是连续存储的,一般指明DIB_RGB_COLORS参数,这样Windows会自动按照pbmi提供的信息分配对应的DIB Section,包括Info Header、Mask、Color Table及Bits四个区,其中分配的Bits区的地址被写到ppvBits指向的指针中。

即常用调用步骤如下:

a.读入DIB的位图信息头(Info Header)、压缩掩码(Mask)及调色板信息(Color Table)到pbmi指向内存中

b.hBitmap = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0);

c.读入DIB的Bits数据到pBits指向的内存中

d.BitBlt或StretchBlt显示hBitmap

3.只有使用DIB_PAL_COLORS参数时才需要hdc参数。只有DIB的调色板使用索引存储方式才需要使用这两个参数。实际上,这里的hdc和DIB_PAL_COLORS实际上最终被SetDIBitsToDevice和StretchDIBits函数调用,可以查看他们两个的参数。

4.pBits指向的内存由Windows操作系统托管,但是用户可以操作pBits数据,删除hBitmap时pBits内存区同时也会释放掉。

2.GetDIBColorTable和SetDIBColorTable

两个函数定义如下:

UINT GetDIBColorTable(
  HDC hdc,          // 设备描述表句柄
  UINT uStartIndex, // 调色板起始索引
  UINT cEntries,    // 要获取的调色板项个数
  RGBQUAD *pColors  // 存储调色板项的地址
);

UINT SetDIBColorTable(
  HDC hdc,          // 设备描述表句柄
  UINT uStartIndex, // 调色板起始索引
  UINT cEntries,    // 要设置的调色板项个数
  RGBQUAD *pColors  // 存储调色板项的地址
);

分别用来获取和设置指定的调色板项,一般如下使用。

hdcMem = CreateCompatibleDC(NULL);
SelectObject(hdcMem, hBitmap);
GetDIBColorTable(hdcMem, uFirstIndex, uNumEntries, &prgb);
DeleteDC(hdcMem);
hdcMem = CreateCompatibleDC(NULL);
SelectObject(hdcMem, hBitmap);
SetDIBColorTable(hdcMem, uFirstIndex, uNumEntries, &prgb);
DeleteDC(hdcMem);

通过DIB Section来获取和设置调色板,可以屏蔽OS/2兼容位图带来的差异(BITMAPCOREINFO调色板项采用RGBTRIPLE结构体而不是BITMAPINFOHEADER采用的RGBQUAD)。

3.获取DIBSECTION

DIBSECTION定义如下:

typedef struct tagDIBSECTION {
    BITMAP              dsBm;
    BITMAPINFOHEADER    dsBmih;
    DWORD               dsBitfields[3];
    HANDLE              dshSection;
    DWORD               dsOffset;
} DIBSECTION;

使用

GetObject(hBitmap, sizeof(DIBSECTION), &dibsection);

不同于BITMAP,DIB Section使用GetObject获取的是DIB Section,可以看到DIBSECTION将BITMAP设为第一个属性,这是为了保证和BITMAP的兼容,万一你不知道hBitmap的属性是指向DIB Section的,那么GetObject(hBitmap, sizeof(BITMAP), &bitmap)也不至于发生错误。

通过DIB Section来获取位图信息,可以不考虑不同DIB位图格式带来的差异,位图信息头均使用BITMAPINFOHEADER,压缩掩码使用DWORD来单独指定,不用考虑BITMAPCOREHEADER、BITMAPV4HEADER、BITMAPV5HWEADER带来的差异。

3.代码演示

在演示程序中,我们读入一幅图片(8bit、16bit、24bit)创建成DIB Section形式显示、查看调色板及压缩掩码

//读入DIB文件并转换成DIB Section
HBITMAP CreateDibSectionFromDibFile(PTSTR szFileName)
{
	BITMAPFILEHEADER	bmfh;
	BITMAPINFO			*pbmi;
	BYTE				*pBits;
	BOOL				bSuccess;
	DWORD				dwInfoSize, dwBytesRead;
	HANDLE				hFile;
	HBITMAP				hBitmap;

	//打开文件
	hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if (INVALID_HANDLE_VALUE == hFile)
	{
		return NULL;
	}

	//读入DIB文件头
	bSuccess = ReadFile(hFile, &bmfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL);
	if (!bSuccess || (dwBytesRead != sizeof(BITMAPFILEHEADER)) || (bmfh.bfType != *(WORD *)"BM"))
	{
		CloseHandle(hFile);
		return NULL;
	}

	//为DIB BITMAPINFO分配内存,并读入DIB数据
	dwInfoSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);

	pbmi = malloc(dwInfoSize);

	if (NULL == pbmi)
	{
		CloseHandle(hFile);
		return NULL;
	}

	bSuccess = ReadFile(hFile, pbmi, dwInfoSize, &dwBytesRead, NULL);

	if (!bSuccess || (dwBytesRead != dwInfoSize))
	{
		free(pbmi);
		CloseHandle(hFile);
		return NULL;
	}

	//创建DIB Section
	hBitmap = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0);
	free(pbmi);

	if (NULL == hBitmap)
	{
		CloseHandle(hFile);
		return NULL;
	}

	//读入位图数据到分配的DIB Section的Bits区(pBits指向)
	bSuccess = ReadFile(hFile, pBits, bmfh.bfSize-bmfh.bfOffBits, &dwBytesRead, NULL);
	CloseHandle(hFile);

	if (!bSuccess || (dwBytesRead != (bmfh.bfSize-bmfh.bfOffBits)))
	{
		return NULL;
	}

	return hBitmap;
}

//如果有调色板则获得第一个调色板项
BOOL GetFirstColorTableItem(HBITMAP hBitmap, RGBQUAD *prgb)
{
	HDC hdcMem;
	int	iNum;

	hdcMem = CreateCompatibleDC(NULL);
	SelectObject(hdcMem, hBitmap);

	iNum = GetDIBColorTable(hdcMem, 0, 1, prgb);
	DeleteDC(hdcMem);

	return 0==iNum ?  FALSE : TRUE;
}

//获得第一个调色板项
DWORD GetFirstMaskItem(HBITMAP hBitmap)
{
	DIBSECTION ds;

	GetObject(hBitmap, sizeof(DIBSECTION), &ds);

	return ds.dsBitfields[0];
}

在后续我会讲解使用DIB Section来完成图像指定像素点的读写,实际上,一旦能够操作pBits,那么完成指定像素点的读写也不是什么难事。

完整演示代码下载链接

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

Windows DIB文件操作详解-4.使用DIB Section,布布扣,bubuko.com

时间: 2024-10-14 04:15:15

Windows DIB文件操作详解-4.使用DIB Section的相关文章

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

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

Windows DIB文件操作详解-5.DIB和调色板

Windows调色板是256色显卡时期的产物,现在显卡最少也是16bit的了,所以调色板基本上是用不到了的. 但是下面几种情况还是需要去使用和了解调色板: 1.在新显卡上保证256色兼容模式的正常运行 2.在256色显卡或低于256色显卡老机器上运行程序或在一些工业控制场合(为了节约成本可能采用256色显卡或低于256色显卡)运行程序 3.操作DIB的指定像素点数据 1.调色板是什么 同样采用下面一张图 如上图,我们可以调色板分为如下几种: 1.DIB文件中的调色板 2.内存中创建的逻辑调色板对

Unix/Linux环境C编程入门教程(41) C语言库函数的文件操作详解

?? 上一篇博客我们讲解了如何使用Linux提供的文件操作函数,本文主要讲解使用C语言提供的文件操作的库函数. 1.函数介绍 fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const char * mode); 函数说明 参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态. mode有下列几种形态字符串: r 打开只读文件,该文件必须存

Android的file文件操作详解

Android的file文件操作详解 android的文件操作要有权限: 判断SD卡是否插入 Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED); 获得sd卡根目录 File skRoot = Environment.getExternalStorageDirectory(); 获得私有根目录 File fileRoot = Context.getFilesDir()+""

Windows DIB文件操作具体解释-4.使用DIB Section

前面讲了为了提高DIB的显示性能和效率,我们将DIB转换成DDB.可是这又遇到一个问题.假设我想操作DIB的数据的话,显然是不能使用DDB:一是由于DIB转DDB时发生了颜色转换.再就是DDB无法直接提取指定像素点的数据.那么我们怎么办呢,Windows使用一种折中的方式来达到这一目标(既提高了显示效率和性能,又能够直接操作像素点). 1.DIB Section存储和显示 Windows使用DIB块(DIB Section)来存储DIB数据.其内存结构示意图例如以下 watermark/2/te

python文件操作详解

文件操作是编程中必不可少的,配置文件,数据存储都是对文件操作:按文件操作与格式主要内容如下图: 文件基本操作为打开,读取,写入,关闭,我们按这个过程来详解讲解. 1.一个例子搞定打开,读取与关闭: 准备工作: 1>新建文件:E:\workdir\readme.txt(或者自己准备一个其他文本文件):2>文件添加一行内容:this is test 直接上代码,有个基本认识: #文件路径 path = r'E:\workdir\readme.txt' #打开文件 f = open(path) #读

【转】 android中的文件操作详解以及内部存储和外部存储

摘要 其实安卓文件的操作和Java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理.根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存储两个概念. 相对 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理.根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存储两个概念

【转】 C语言文件操作详解

转自:http://www.cnblogs.com/likebeta/archive/2012/06/16/2551780.html C语言中没有输入输出语句,所有的输入输出功能都用 ANSI C提供的一组标准库函数来实现.文件操作标准库函数有: 文件的打开操作 fopen 打开一个文件 文件的关闭操作 fclose 关闭一个文件 文件的读写操作 fgetc 从文件中读取一个字符 fputc 写一个字符到文件中去 fgets 从文件中读取一个字符串 fputs 写一个字符串到文件中去 fprin

android中的文件操作详解以及内部存储和外部存储(转载)

原文链接:http://m.blog.csdn.net/article/details?id=17725989 摘要 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理.根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存储两个概念. 相对 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的ap