我正在使用System.Drawing.Bitmap
,需要访问单个像素,但GetPixel()
和SetPixel()
对我来说太慢了。
使用此答案中描述的技术,我正在分配一个byte[]
,将其固定在内存中,将我的原始Bitmap
复制到其中,然后取消固定它。然后我使用byte[]
,然后固定它,用缓冲区构造一个新Bitmap
,然后保存它。
我计算缓冲区的大小(以字节为单位)为width * height * BytesPerPixel
.我正在使用PixelFormat.Format32bppArgb
,所以BytesPerPixel == 4
.
这一切都工作正常,除了我显然分配了一个太小的缓冲区并在复制位图时导致访问冲突。我必须添加额外的空间(很多)以避免访问违规,一切似乎都正常。
如何计算_buffer
所需的合适尺寸?
编辑:我刚刚发现原始图像是PixelFormat.Format24bppRgb
。但是,这是每个像素 3 个字节,我认为我的缓冲区应该足够大。
这是一个完整的程序来演示问题。ExtraSpace
控制我必须分配的额外字节...:
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace FastImageTest
{
class FastImage
{
private const int BytesPerPixel = 4;
private readonly byte[] _buffer;
private readonly int _width;
private readonly int _height;
private const int ExtraSpace = 0;// 1024 * 1024 * 256;
public FastImage(Bitmap from)
{
_width = from.Width;
_height = from.Height;
_buffer = new byte[_width * _height * BytesPerPixel + ExtraSpace];
GCHandle handle = GCHandle.Alloc(_buffer);
try
{
var address = Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, 0);
var bitsPerPixel = 32;
var stride = bitsPerPixel * _width;
var pixelFormat = PixelFormat.Format32bppArgb;
//***** Access violation occurs on next line
using (var fastBitmap = new Bitmap(_width, _height, stride, pixelFormat, address))
{
using (var fastBitmapGraphics = Graphics.FromImage(fastBitmap))
fastBitmapGraphics.DrawImageUnscaled(from, 0, 0);
}
}
finally
{
handle.Free();
}
}
}
class Program
{
static void Main(string[] args)
{
var original = new Bitmap(@"d:pic.jpg");
var fast = new FastImage(original);
}
}
}
请记住,Windows 位图对象要求步幅与 DWORD(4 字节)对齐。 宽度不是 4 倍的 24 位图像的步幅将不同于 4 * 宽度。
另请注意,步幅以字节为单位,而不是位,因此您的计算在任何情况下都是错误的。您告诉Bitmap
类它有32 * width
步幅,这是应有的 8 倍,因此 GDI+ 可能会在图像的 1/8 时耗尽有效内存地址(如果你幸运的话......如果你不是,它会将随机数据写入重要的地方)。
计算应如下所示:
int stride = 4 * ((_width * bytesPerPixel + 3) / 4);
注意bytesPerPixel
而不是bitsPerPixel
。确保这是根据位图格式正确计算的,而不仅仅是设置为 4 的 const。