我正在解码一个.BMP文件,此时我需要处理16位颜色。整个代码库使用32位颜色(R、G、B、A(,因此我需要将颜色转换为24位RGB值(每种颜色一个字节(。
根据规范,颜色的每个分量是5位(浪费1位(。我的代码如下:
ushort color = BitConverter.ToUInt16(data, 54 + i);
byte blue = (byte)((color | 0b0_00000_00000_11111) / 31f * 255);
byte green = (byte)(((color | 0b0_00000_11111_00000) >> 5) / 31f * 255);
byte red = (byte)(((color | 0b0_11111_00000_00000) >> 10) / 31f * 255);
然而,这似乎并不是特别有效。我尝试过color << (8 - 5)
,它使处理速度更快,并避免了浮点转换,但它并不准确——值31(11111(转换为248。有没有其他比特操作破解的方法可以实现这一点,或者我被迫将每个数字转换为浮点值来改变颜色空间?
不仅可以避免浮点转换,还可以避免乘法和除法。从我的实现来看:
internal struct Color16Rgb555
{
private const ushort redMask = 0b01111100_00000000;
private const ushort greenMask = 0b00000011_11100000;
private const ushort blueMask = 0b00011111;
private ushort _value;
internal Color16Rgb555(ushort value) => _value = value;
internal byte R => (byte)(((_value & redMask) >> 7) | ((_value & redMask) >> 12));
internal byte G => (byte)(((_value & greenMask) >> 2) | ((_value & greenMask) >> 7));
internal byte B => (byte)(((_value & blueMask) << 3) | ((_value & blueMask) >> 2));
}
用法:
var color = new Color16Rgb555(BitConverter.ToUInt16(data, 54 + i));
byte blue = color.B;
byte green = color.G;
byte red = color.R;
它为31产生255,因为它用实际5比特值的3个MSB比特填充剩余的3比特。
但是假设你的data
是一个字节数组,如果你使用我的绘图库,你有一个更方便的选择:
// to interpret your data array as 16BPP pixels with RGB555 format:
var my16bppBitmap = BitmapDataFactory.CreateBitmapData(
data, // your back buffer
new Size(pixelWidth, pixelHeight), // size in pixels
stride, // the size of one row in bytes
KnownPixelFormat.Format16bppRgb555);
// now you can get/set pixels normally
Color somePixel = my16bppBitmap.GetPixel(0, 0);
// For better performance obtain a row first.
var row = my16bppBitmap[0]; // or FirstRow (+MoveNextRow if you wish)
Color32 asColor32 = row[0]; // accessing pixels regardless of PixelFormat
ushort asUInt16 = row.ReadRaw<ushort>(0); // if you know that it's a 16bpp format