我尝试了使用非托管代码的手动方法 - 本地基准测试表明它比无知 (Bitmap.GetPixel > Bitmap.SetPixel) 方法快 99.7%。
基本上,我们使用LockBits 指针并根据调色板一一分配字节。
static unsafe void To24Bpp(Bitmap source, Bitmap dest)
{
var sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly,
PixelFormat.Format8bppIndexed);
var destData = dest.LockBits(new Rectangle(0, 0, dest.Width, dest.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
var paletteBytes = source.Palette.Entries.Select(ColorToUintRgbLeftAligned).ToArray();
var current = (byte*) sourceData.Scan0.ToPointer();
var lastPtr = (byte*) (sourceData.Scan0 + sourceData.Width*sourceData.Height).ToPointer();
var targetPtr = (byte*) destData.Scan0;
while (current <= lastPtr)
{
var value = paletteBytes[*current++];
targetPtr[0] = (byte) (value >> 24);
targetPtr[1] = (byte) (value >> 16);
targetPtr[2] = (byte) (value >> 8);
targetPtr += 3;
}
source.UnlockBits(sourceData);
dest.UnlockBits(destData);
}
static uint ColorToUintRgbLeftAligned(Color color)
{
return ((uint) color.B << 24) + ((uint) color.G << 16) + ((uint) color.R << 8);
}
可以改进代码以从调色板一次写入 4 个字节,从而减少随机内存访问量。我的本地基准测试显示,它的性能进一步提高了 25%。请注意构建 uint 颜色字节的区别 - uint 中的字节对齐方式与我的预期相反。
private static unsafe void To24BppUintAssignment(Bitmap source, Bitmap dest)
{
var sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
var destData = dest.LockBits(new Rectangle(0, 0, dest.Width, dest.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
uint[] paletteBytes = source.Palette.Entries.Select(ColorToUintRgbRightAligned).ToArray();
var current = (byte*)sourceData.Scan0.ToPointer();
var lastPtr = (byte*)(sourceData.Scan0 + sourceData.Width * sourceData.Height).ToPointer();
var targetPtr = (byte*) destData.Scan0;
while (current < lastPtr)
{
var targetAsUint = ((uint*) targetPtr);
targetAsUint[0] = paletteBytes[*current++];
targetPtr += 3;
}
uint finalValue = paletteBytes[*current];
targetPtr[0] = (byte)(finalValue >> 24);
targetPtr[1] = (byte)(finalValue >> 16);
targetPtr[2] = (byte)(finalValue >> 8);
source.UnlockBits(sourceData);
dest.UnlockBits(destData);
}
private static uint ColorToUintRgbRightAligned(Color color)
{
return ((uint)color.B) + ((uint)color.G << 8) + ((uint)color.R << 16);
}
我没有在方法中创建位图以进行基准测试,它应该这样调用:
static Bitmap To24Bpp(Bitmap source)
{
var dest = new Bitmap(source.Width, source.Height, PixelFormat.Format24bppRgb);
To24BppUintAssignment(source, dest);
return dest;
}