最近使用Directx对图像进行显示,出现图像乱码的现象,研究发现创建Texture的时候指定的图片宽度和锁定的纹理表面的宽度(LockedRect.Pitch)不一致,导致图片纹理是乱码。下面通过两种方式创建纹理分析LockedRect和最终纹理宽高之间的关系,一是通过图片的数据创建纹理,二是直接通过已知图片的像素点数据来创建数据。
LockedRect数据结构
|
|
Pitch
表示纹理表面一行所包含的byte,也就是一行纹理的长度,在使用的过程当中我们会发现这个长度必须是位宽的整数倍,也就保证和显存位宽对齐,这个和显卡有关系,常见的有64bit、128bit和256bit等;pBits
表示一个指针,指向我们锁定表面纹理的数据;
如果不考虑图片的宽和LockedRect.Pitch之间的关系,会很容易出现纹理最终显示出来的图片乱码
通过RGB数据生成纹理
首先通过一个例子,已知我们有图像的RGB数据,存储在imgData数组里面,我们要将RGB数据生成图片,该如何做?
解题流程:创建纹理 —> 锁定纹理表面 —> 对表面进行填充 —> 解锁表面 —> 生成纹理
|
|
其实以上代码是有错误的,
- 1、以二维图片的存储的思维,好像是没有错的,但是实际上计算机内存里面比不是二维储存的,而是一维存储方式;
- 2、将数据从内存拷贝到纹理当中又会不一样,在显存中纹理会有一个固定的位宽,这个位宽由电脑硬件决定,一般有128bit,64bit等;
所以我们要将上面for循环里面的代码进行修改:
这里要注意几点,
注意一: 在创建纹理的时候,我们明明使用的纹理格式是R8G8B8,但是最后生成纹理的时候,每个像素点仍然占用4个字节,所以我们如果有alpha通道的数值可以填充进去,因此代码可以再进行修改如下;
123456int iDest = i * LockedRect.Pitch + j * 4;int iSrc = i * imgWidth * 4 + j * 4;for (int k = 0; k < 4; k++){pSource[x + k] = imgData[x + k];}注意二: 不能错误地把创建纹理表面的宽度和图片的宽度划等号,他们并不相等,他们之间的关系使用公式
LockedRect.Pitch = 128*ceil(imgWidth*4/128)
。
以我电脑显卡为例,创建表面的宽度是128的倍数,即LockedRect.Pitch
是128的倍数,
那么问题来了,假如我现在的图片宽度是2160,按正常的4通道图片来算应该是2160 * 4 = 8640
,但是我们锁定的纹理表面的宽度LockedRect.Pitch = 8704 = 128 * ceil(2160*4/128)
,
这样就多出了64字节的空间,怎么办?其实不用管,不用进行任何操作。
使用已知图片的RGB数据创建纹理
下面是通过图片创建纹理,这个例子更简单,只要保证每次拷贝一行数据到纹理对应的一行就行了,