将像素缓冲区从16位转换为B8G8R8A8_UNorm



因此,我从外部原生库(c++)中获得了一个像素缓冲区,它看起来是16Bit RGB(SlimDX等效于B5G6R5_UNorm)。

我想使用Direct2D显示由该缓冲区表示的图像。但是Direct2D不支持B5G6R5_UNorm

所以我需要将这个像素缓冲区转换为B8G8R8A8_UNorm

我看到过这样一个任务中使用位转换方法的各种代码片段,但没有一个是特定于我的需求或格式的。我没有任何关于比特移位的线索,也没有任何关于如何移位的线索。

我追求的是C♯这样一个任务的代码示例或任何进行转换的内置方法-我不介意使用其他库的

请注意:我知道这可以使用C♯位图类,但我尽量不依赖这些内置类(GDI有一些我不喜欢的地方),图像(以像素缓冲区的形式)会很厚很快,我选择SlimDX是因为它的易用性和性能。

我认为我需要转换像素缓冲区的原因是,如果我使用B8G8R8A8_UNorm绘制图像,则图像具有绿色覆盖,并且像素遍布各处,因此我认为我首先需要将像素缓冲区转换或"升级"为所需格式。

补充一下:当我在不转换缓冲区的情况下进行上述操作时,图像不会填充整个几何体。

像素缓冲器通过byte[]对象提供

位移位和逻辑运算符在处理图像格式时非常有用,因此您有义务阅读更多关于它的信息。不过,我可以快速介绍一下这种像素格式所代表的内容,以及如何从一种格式转换为另一种格式。我应该在回答之前警告一下,我真的不太了解C#及其支持库,所以可能有一个开箱即用的解决方案。

首先,像素缓冲区的格式为B5G6R5_UNORM。因此,我们为每个像素分配了16个比特(5个红色、6个绿色和5个蓝色)。我们可以将此像素格式的位布局可视化为"RRRRR GGGGGG BBBBB",其中"R"代表属于红色通道的位,"G"代表属于绿色通道的位和"B"代表属于蓝色通道的位。

现在,假设像素缓冲区的前16位(两个字节)是111111 0100101111。将其与像素格式的位布局对齐。。。

RRRRRGGGGGGBBBBB
1111110100101111

这意味着红色通道具有比特11111,绿色通道具有比特101001,蓝色通道具有比特01111。从二进制转换为十进制:红色=31,绿色=41,蓝色=15。您会注意到红色通道的所有位都设置为1,但其值(31)实际上小于绿色通道(41)。然而,这并不意味着当显示时颜色是绿色多于红色;绿色通道有一个额外的位,所以它可以表示比红色和蓝色通道更多的值,但在这个特定的例子中,输出颜色中实际上有更多的红色!这就是内罗毕办事处的作用…

UNORM代表无符号归一化整数;这意味着颜色通道值将被解释为0.0到1.0之间的等距浮点数。这些值通过分配的位数进行归一化。这到底意味着什么?假设你有一个只有3位的格式来存储一个频道。这意味着通道可以有2^3=8个不同的值,如下所示,分别用十进制、二进制和归一化表示。归一化值只是十进制值除以可以用N位表示的最大可能的十进制值。

Decimal | Binary | Normalized
-----------------------------
0       | 000    | 0/7 =  0.000
1       | 001    | 1/7 =~ 0.142
2       | 010    | 2/7 =~ 0.285
3       | 011    | 3/7 =~ 0.428
4       | 100    | 4/7 =~ 0.571
5       | 101    | 5/7 =~ 0.714
6       | 110    | 6/7 =~ 0.857
7       | 111    | 7/7 =  1.000

回到前面的例子,其中像素具有位111111 0100101111,我们已经知道三个颜色通道的十进制值:RGB={31,41,15}。我们想要标准化的值,因为十进制值具有误导性,在不知道它们存储了多少位的情况下不会告诉我们太多。红色和蓝色通道存储有5位,因此最大的十进制值为2^5-1=31;但是,绿色通道的最大十进制值是2^6-1=63。知道了这一点,标准化的颜色通道是:

// NormalizedValue = DecimalValue / MaxDecimalValue
R = 31 / 31 =  1.000
G = 41 / 63 =~ 0.650
B = 15 / 31 =~ 0.483

重申一下,归一化值很有用,因为它们表示输出中每个颜色通道的相对贡献。在给定的通道中添加更多的比特不会影响可能的颜色范围,它只是提高了颜色精度(基本上是该颜色通道的更多阴影)。

了解了以上所有内容后,您应该能够从任何RGB(A)格式转换为任何其他RGB(B)格式,而不管每个通道中存储了多少位。例如,让我们将刚才计算的归一化值转换为B8G8R8A8_UNORM。一旦计算了标准化值,这就很容易了,因为您只需按新格式中的最大值进行缩放。每个通道使用8位,因此最大值为2^8-1=255。由于原始格式没有alpha通道,因此通常只存储最大值(表示完全不透明)。

// OutputValue = InputValueNormalized * MaxOutputValue
B = 0.483 * 255 = 123.165
G = 0.650 * 255 = 165.75
R = 1.000 * 255 = 255
A = 1.000 * 255 = 255

在编写代码之前,现在只缺少一件事。在上面,我可以通过排列并复制每个频道的比特。这就是我得到101001绿色部分的原因。在代码中,这可以通过"屏蔽"掉我们不关心的部分来实现。移位正是它听起来的样子:它将比特向右或向左移动。当您向右移动位时,最右边的位将被丢弃,而新的最左边的位将分配0。下面使用上面的16位示例进行可视化。

1111110100101111 // original 16 bits
0111111010010111 // shift right 1x
0011111101001011 // shift right 2x
0001111110100101 // shift right 3x
0000111111010010 // shift right 4x
0000011111101001 // shift right 5x

你可以不断变换,最终你会得到十六个0。然而,我停在五个班次是有原因的。现在请注意,最右边的6个比特是绿色比特(我已经移位/丢弃了5个蓝色比特)。我们几乎已经提取出了我们需要的确切比特,但在绿色比特的左边还有额外的5个红色比特。为了去除这些,我们使用"逻辑和"运算只屏蔽最右边的6位。掩码的二进制值为00000000001111111;1表示我们想要这个位,0表示我们不想要。掩码除了最后6个位置外都是0,因为我们只想要最后6个位。将此掩码与5倍移位的数字对齐,当两位都为1时,输出为1,每隔一位为0:

0000011111101001 // original 16 bits shifted 5x to the right
0000000000111111 // bit mask to extract the rightmost 6 bits
------------------------------------------------------------
0000000000101001 // result of the 'logical and' of the two above numbers

结果正是我们要寻找的数字:6个绿色比特,没有其他东西。回想一下,前面的0对十进制值没有影响(它仍然是41)。在C#或任何其他类C语言中执行"右移"(>>)和"逻辑和"(&)操作非常简单。以下是它在C#中的样子:

// 0xFD2F is 1111110100101111 in binary
uint pixel = 0xFD2F;
// 0x1F is 00011111 in binary (5 rightmost bits are 1)
uint mask5bits = 0x1F;
// 0x3F is 00111111 in binary (6 rightmost bits are 1)
uint mask6bits = 0x3F;
// shift right 11x (discard 5 blue + 6 green bits), then mask 5 bits
uint red   = (pixel >> 11) & mask5bits;
// shift right 5x (discard 5 blue bits), then mask 6 bits
uint green = (pixel >> 5)  & mask6bits;
// mask 5 rightmost bits
uint blue  = pixel & mask5bits;

把所有这些放在一起,你可能会得到一个看起来类似的例程。但是,一定要仔细阅读endianness,以确保字节按照您期望的方式排序。在这种情况下,参数是一个32位无符号整数(忽略前16位)

byte[] R5G6B5toR8G8B8A8(UInt16 input)
{
return new byte[]
{
(byte)((input & 0x1F) / 31.0f * 255),         // blue
(byte)(((input >> 5) & 0x3F) / 63.0f * 255),  // green
(byte)(((input >> 11) & 0x1F) / 31.0f * 255), // red
255                                           // alpha
};
}

最新更新