从像素数据的字节数组创建位图

这个问题是关于如何读取/写入,分配和管理位图的像素数据。


这是一个如何为像素数据分配字节数组(托管内存)并使用它创建位图的示例:


Size size = new Size(800, 600);

PixelFormat pxFormat = PixelFormat.Format8bppIndexed;

//Get the stride, in this case it will have the same length of the width.

//Because the image Pixel format is 1 Byte/pixel.

//Usually stride = "ByterPerPixel"*Width

//但这并不总是正确的。在bobpowell的更多信息。


int stride = GetStride(size.Width, pxFormat);

byte[] data = new byte[stride * size.Height];

GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);

Bitmap bmp = new Bitmap(size.Width, size.Height, stride,

             pxFormat, handle.AddrOfPinnedObject());


//After doing your stuff, free the Bitmap and unpin the array.

bmp.Dispose();

handle.Free();


public static int GetStride(int width, PixelFormat pxFormat)

{

    //float bitsPerPixel = System.Drawing.Image.GetPixelFormatSize(format);

    int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;

    //Number of bits used to store the image data per line (only the valid data)

    int validBitsPerLine = width * bitsPerPixel;

    //4 bytes for every int32 (32 bits)

    int stride = ((validBitsPerLine + 31) / 32) * 4;

    return stride;

}

我以为该位图可以复制数组数据,但实际上它指向相同的数据。您可以看到:


Color c;

c = bmp.GetPixel(0, 0);

Console.WriteLine("Color before: " + c.ToString());

//Prints: Color before: Color [A=255, R=0, G=0, B=0]

data[0] = 255;

c = bmp.GetPixel(0, 0);

Console.WriteLine("Color after: " + c.ToString());

//Prints: Color after: Color [A=255, R=255, G=255, B=255]

问题:


从byte []数组(托管内存)和free()GCHandle创建位图是否安全?如果不安全,则需要保留固定的阵列,这对GC / Performance有多严重?


更改数据是否安全(例如:data [0] = 255;)?


GC可以更改Scan0的地址吗?我的意思是,我从锁定的位图获取Scan0,然后将其解锁,再过一段时间锁定后,Scan0会有所不同吗?


LockBits方法中ImageLockMode.UserInputBuffer的用途是什么?很难找到有关该信息!MSDN没有清楚地解释它!

繁星点点滴滴
浏览 574回答 3
3回答

宝慕林4294392

关于你的问题4:在ImageLockMode.UserInputBuffer能给你可能被引用到的那些记忆大量的分配过程的控制BitmapData对象。如果选择创建自己的BitmapData对象,则可以避免使用Marshall.Copy。然后,您将不得不将此标志与另一个标志结合使用ImageLockMode。注意这是一项复杂的业务,特别是与Stride和PixelFormat有关。这是一个示例,将一张照片中的24bbp缓冲区的内容放入一个BitMap中,然后另一张照片将其读回到另一个缓冲区中,并将其读入48bbp。Size size = Image.Size;Bitmap bitmap = Image;// myPrewrittenBuff is allocated just like myReadingBuffer below (skipped for space sake)// But with two differences: the buff would be byte [] (not ushort[]) and the Stride == 3 * size.Width (not 6 * ...) because we build a 24bpp not 48bppBitmapData writerBuff= bm.LockBits(new Rectangle(0, 0, size.Width, size.Height), ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb, myPrewrittenBuff);// note here writerBuff and myPrewrittenBuff are the same referencebitmap.UnlockBits(writerBuff);// done. bitmap updated , no marshal needed to copy myPrewrittenBuff // Now lets read back the bitmap into another format...BitmapData myReadingBuffer = new BitmapData();ushort[] buff = new ushort[(3 * size.Width) * size.Height]; // ;Marshal.AllocHGlobal() if you wantGCHandle handle= GCHandle.Alloc(buff, GCHandleType.Pinned);myReadingBuffer.Scan0 = Marshal.UnsafeAddrOfPinnedArrayElement(buff, 0);myReadingBuffer.Height = size.Height;myReadingBuffer.Width = size.Width;myReadingBuffer.PixelFormat = PixelFormat.Format48bppRgb;myReadingBuffer.Stride = 6 * size.Width;// now read into that buffBitmapData result = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height), ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly, PixelFormat.Format48bppRgb, myReadingBuffer);if (object.ReferenceEquals(result, myReadingBuffer)) {    // Note: we pass here    // and buff is filled}bitmap.UnlockBits(result);handle.Free();// use buff at will...如果您使用ILSpy,则会看到此方法链接到GDI +,这些方法的帮助更加完整。您可以通过使用自己的内存方案来提高性能,但是请注意,Stride可能需要进行一些调整才能获得最佳性能。然后,您将可以例如分配巨大的虚拟内存映射的scan0并非常有效地blit它们。请注意,固定巨大的数组(尤其是少数数组)不会给GC造成负担,并且可以让您以完全安全的方式(或者如果您追求速度则不安全)来操纵字节/短路。

慕沐林林

我不确定您是否会按照自己的方式进行操作。也许有。似乎您走得太远了,所以您可能要尝试做一些比问题标题所暗示的要高级的事情...但是,从字节数组创建位图的传统方法是:using (MemoryStream stream = new MemoryStream(byteArray)){     Bitmap bmp = new Bitmap(stream);     // use bmp here....}
打开App,查看更多内容
随时随地看视频慕课网APP