Direct3D9 - 将 A2R10G10B10 图像转换为 A8R8G8B8 图像

Direct3D9 - Convert a A2R10G10B10 image to a A8R8G8B8 image

开始前:

A2B10G10R10 (2 bits for the alpha, 10 bits for each color channels)
A8B8G8R8 (8 bits for every channels)

如果我错了请指正,但是A2B10G10R10像素格式不能直接在屏幕上显示是对的吗?

如果是这样,我想使用 OpenCV、Direct3D9 甚至手动将我的 A2B10G10R10 图像转换为可显示的 A8B8G8R8 图像,但我在按位运算方面真的很糟糕,这就是我需要你帮助的原因。

所以我在这里:

// Get the texture bits pointer
offscreenSurface->LockRect(&memDesc, NULL, 0);
// Copy the texture bits to a cv::Mat 
cv::Mat m(desc.Height, desc.Width, CV_8UC4, memDesc.pBits, memDesc.Pitch);
// Convert from A2B10G10R10 to A8B8G8R8
???

这里我认为我应该如何处理每个 32 位包:

  1. 将原始的第一个2位复制到第一个转换后的8位
  2. 将每个原始 10 位缩放到每个转换后的 8 位(如何做到这一点?)对于所有其他通道

注:

所以要恢复,问题是: 如何将 A2B10G10R10 像素格式纹理转换为 A8B8G8R8 像素格式纹理?

谢谢。最好的问候。

我不确定您为什么使用旧版 Direct3D 9 而不是 DirectX 11。无论如何,Direct3D 9 时代 D3DFMT 和现代 DXGI_FORMAT 之间的命名方案被翻转了,所以可能有点混乱。

  • D3DFMT_A8B8G8R8 等同于 DXGI_FORMAT_R8G8B8A8_UNORM
  • D3DFMT_A2B10G10R10 等同于 DXGI_FORMAT_R10G10B10A2_UNORM
  • D3DFMT_A8R8G8B8 等同于 DXGI_FORMAT_B8G8R8A8_UNORM

  • 在 DXGI 中没有 D3DFMT_A2R10G10B10 的直接等价物,但您可以交换 red/blue 通道来获得它。

There's also a long-standing bug in the deprecated D3DX9, D3DX10, and D3DX11 helper libraries where the DDS file format DDPIXELFORMAT have the red and blue masks backwards for both 10:10:10:2 formats. My DDS texture readers solve this by flipping the mapping of the masks to the formats on read, and always writing DDS files using the more modern DX10 header where I explicitly use DXGI_FORMAT_R10G10B10A2_UNORM . See this post for more details.

将 10:10:10:2 转换为 8:8:8:8 的最大问题是您丢失了 R、G、B 颜色通道的 2 位数据。你可以做一个天真的位移,但结果通常是垃圾。要处理失去精度的颜色转换,您需要使用 error diffusion or ordered dithering.

之类的东西

此外,对于 2 位 alpha,您不希望 3 (11) 映射到 192 (11000000),因为在 2 位 alpha 中,3“11”是完全不透明的而 255 (11111111) 是 8 位 alpha。

看看 DirectXTex,这是一个开源库,可以为每个 DXGI_FORMAT 进行转换,并且可以处理大多数 D3DFMT 的遗留转换。它实现了我刚才提到的所有内容。

库使用 float4 中间值,因为它建立在 DirectXMath 之上,并且提供了比一堆特殊情况转换组合更通用的解决方案。对于特殊情况的高性能使用,您可以编写一个直接的 10 位到 8 位转换器,并带有所有抖动,但这是一种非常不寻常的情况。

通过所有关于格式图像转换的讨论,您实际上可以将 10:10:10:2 纹理渲染到 8:8:8:8 渲染目标上展示。您也可以使用 10:10:10:2 作为渲染目标后备缓冲区格式,它会被转换为 8:8:8:8 作为当前格式的一部分。 10:10:10:2 的硬件支持在 Direct3D 9 上是可选的,但在使用 DirectX 11 时 Direct3D Feature Level 10 或更好的卡是必需的。使用 [=60= 时甚至可以获得真正的 10 位显示扫描输出] 全屏渲染模式,Windows 10 将在今年晚些时候本地实现 HDR 显示。

对此有一个通用的解决方案,能够立即完成它而无需合并庞大的库或引入新的渲染代码(有时甚至不实用)是很好的。

首先,拆开它。我永远无法跟踪 RGBA 字段的顺序,所以我只是尝试各种方法直到一个有效,这种策略每次都能可靠地工作......最终。但是您不妨相信您的分析是您第一次尝试的结果。我发现的文档说 D3D 将它们从 MSB 列出到 LSB,所以在这种情况下我们有 %AA RRRRRRRRRR GGGGGGGGGG BBBBBBBBBB(但我不知道这是否正确)

b = (src>> 0) & 0x3FF;
g = (src>>10) & 0x3FF;
r = (src>>20) & 0x3FF;
a = (src>>30) & 0x003;

接下来,您修复精度。朴素的位移位经常可以正常工作。如果结果是每个通道 8 位,那么您的情况不会比大多数图像差。从 10 位向下移动到 3 位在没有抖动的情况下看起来很糟糕,但从 10 位向下移动到 8 位看起来不错。

r >>= 2; g >>= 2; b >>= 2;

现在 alpha 组件确实变得很棘手,因为它正在向另一个方向移动。正如@chuck-walbourn 所说,您需要考虑如何映射 alpha 值。这可能是您想要的:

%00 -> %00000000
%01 -> %01010101
%10 -> %10101010
%11 -> %11111111

虽然大小为 4 的查找 table 在这里可能最有意义,但还有一种更通用的方法。你所做的是将你的小值推到大值的顶部,然后复制它。这是你的场景和另一个更有趣的场景:

%Aa -> %Aa Aa Aa Aa 
%xyz -> %xyz xyz xy

让我们通过查找检查 xyz 会发生什么 table:

%000 -> %000 000 00 (0)
%001 -> %001 001 00 (36) +36
%010 -> %010 010 01 (73) +37
%011 -> %011 011 01 (109) +36
%100 -> %100 100 10 (146) +37
%101 -> %101 101 10 (182) +36
%110 -> %110 110 11 (219) +37
%111 -> %111 111 11 (255) +36

如您所见,我们通过这种方法获得了一些很好的特性。 %00000000%11111111 的结果自然是最重要的。

接下来我们打包结果:

dst = (a<<24)|(r<<16)|(g<<8)|(b<<0);

然后我们决定是否需要对其进行优化,或者看看编译器做了什么并摸不着头脑。