System.Drawing.Bitmap包含的数据多于预期

时间:2012-07-12 01:46:28

标签: c# graphics gdi+

我有一种方法可以将数据复制出System.Drawing.Bitmap,如下所示:

var readLock = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte[] data = new byte[3 * image.Width * image.Height];
if (data.Length != readLock.Stride * readLock.Height)
    throw new InvalidOperationException("Incorrect number of bytes");
Marshal.Copy(readLock.Scan0, data , 0, data.Length);
image.UnlockBits(readLock);

非常简单,它适用于我的大多数图像。然而,对于非常小的图像(14x14),它会遇到异常。在失败的情况下,Stride是44,而不是预期的42(14 * 3)。

像素格式为Format24bppRgb,因此图像中的每个像素应该有三个字节。这些额外字节来自何处,以及在处理图像数据时如何处理它们?

对于任何有兴趣的人,我都是从高度图生成普通数据,因此我需要能够准确地获取每个像素及其邻居。

1 个答案:

答案 0 :(得分:4)

Bitmap的每个像素行必须对齐,这就是为什么stride并不总是width * bytes-per-pixel的原因。你应该忽略任何额外的字节。这意味着如果您使用的是具有未对齐数据的字节数组,则可能无法始终在单个Marshal.Copy()调用中复制所有图像数据。每行像素都从readLock.Scan0 + y * readLock.Stride开始,包含readLock.Width * bytes-per-pixel个有意义的字节。

解决方案:

const int BYTES_PER_PIXEL = 3;
var data = new byte[readLock.Width * readLock.Height * BYTES_PER_PIXEL];
if(readLock.Stride == readLock.Width * BYTES_PER_PIXEL)
{
    Marshal.Copy(readLock.Scan0, data, 0, data.Length);
}
else
{
    for(int y = 0; y < readLock.Height; ++y)
    {
        IntPtr startOfLine = (IntPtr)((long)readLock.Scan0 + (readLock.Stride * y));
        int dataOffset = y * readLock.Width * BYTES_PER_PIXEL;
        Marshal.Copy(startOfLine, data, dataOffset, readLock.Width * BYTES_PER_PIXEL);
    }
}