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

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

大家好,又见面了,我是全栈君。

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

1.DIB Section存储和显示

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

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

事实上,和我们自己读入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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115221.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux下安装ffmpeg_linux 当前时间

    linux下安装ffmpeg_linux 当前时间linuxffmpeg安装ffmpeg安装不会吧这都22年了还有人问ffmpeg安装第一步我们先去下载资源包这里是4.4版本wgethttp://www.ffmpeg.org/releases/ffmpeg-4.4.tar.gz第二步解压资源包tar-zxvfffmpeg-4.4.tar.gz第3步创建安装目录我是安装在##我是安装在/www/server/ffmpeg下面mkdirffmpeg第4步返回解压目录cdffmpeg-4.

    2025年11月4日
    3
  • docker部署Discuz论坛

    docker部署Discuz论坛

    2021年5月29日
    159
  • pycharm opencv安装_pycharm自带python

    pycharm opencv安装_pycharm自带python一、介绍下我的环境:1、win102、python3.53、pycharm3.6二、安装要在pycharm中安装cv2模块,但是没安装openCV前安装失败(安装模块方法:pycharm导入模块)1、openCV库下载:https://www.lfd.uci.edu/~gohlke/pythonlibs/Ctrl+F输入opencv,下载对应版本opencv…

    2022年8月27日
    6
  • Spring cloud多模块开发下Feign的使用,以及@FeignClient注入bean找不到异常解决「建议收藏」

    Spring cloud多模块开发下Feign的使用,以及@FeignClient注入bean找不到异常解决「建议收藏」一、关于Feign在微服务架构开发是,我们常常会在一个项目中调用其他服务,其实使用SpringCloudRbbon就能实现这个需求,利用RestTemplate的请求拦截来实现对依赖服务的接口调用,但是实际项目中对服务依赖的调用可能不止于一处,往往一个接口会被多处调用,所以我们通常都会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用。这个时候我们会发现,由于R…

    2022年5月6日
    410
  • ASP.NET 使用Ajax

    ASP.NET 使用Ajax之前在Ajax初步理解中介绍了对Ajax的初步理解,本文将介绍在ASP.NET中如何方便使用Ajax,第一种当然是使用jQuery的ajax,功能强大而且操作简单方便,第二种是使用.NET封装好的Sc

    2022年7月4日
    26
  • JS 面试题 大全

    JS 面试题 大全1、介绍一下js的数据类型有哪些,值是如何存储的?2、说一下js的数据类型的转换都有哪些?3、如何去判断js数据类型?4、介绍js有哪些内置对象?5、javascript创建对象的几种方式?6、js获取原型的方法?7、什么是闭包,为什么要用它?8、三种事件模型是什么?9、哪些操作会造成内存泄漏?10、简述javascript中this的指向?![在这里插入图片描述](https://img-blog.csdnimg.cn/2021032219142296.png

    2022年8月26日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号