【发布时间】:2017-03-17 10:41:43
【问题描述】:
我正在使用System.Drawing.Bitmap,需要访问单个像素,但GetPixel() 和SetPixel() 对我来说太慢了。
使用the technique described in this answer,我正在分配一个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);
}
}
}
【问题讨论】:
-
@PeterDuniho 感谢您提供的信息,我会检查这些内容。我不认为我的步幅计算有误,那是
BytesPerPixel,而不是Bits。并感谢链接到 MVCE。这是一个完整的例子,我只是不知道没有原始图像就不一定可以重现。 -
var stride = bitsPerPixel * _width其中var bitsPerPixel = 32。错了。 -
啊,我现在明白了!对不起..
-
@PeterDuniho 你说得对,步幅是我的问题。我在评论中非常确定我使用的是
BytesPerPixel。如果您想将其发布为答案,我会接受。