Crear mapa de bits a partir de una matriz de bytes de datos de píxeles

Crear mapa de bits a partir de una matriz de bytes de datos de píxeles
  1. Es seguro si ordena. Copia datos en lugar de configurar scan0 (directamente o a través de esa sobrecarga de BitMap()). No desea mantener anclados los objetos administrados, esto limitará el recolector de elementos no utilizados.
  2. Si copia, perfectamente seguro.
  3. La matriz de entrada está administrada y el GC puede moverla, scan0 es un puntero no administrado que quedaría obsoleto si la matriz se moviera. El objeto de mapa de bits en sí mismo se administra, pero establece el puntero scan0 en Windows a través de un identificador.
  4. ImageLockMode.UserInputBuffer es? Aparentemente, se puede pasar a LockBits, tal vez le dice a Bitmap() que copie los datos de la matriz de entrada.

Código de ejemplo para crear un mapa de bits en escala de grises a partir de una matriz:

    var b = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);

    ColorPalette ncp = b.Palette;
    for (int i = 0; i < 256; i++)
        ncp.Entries[i] = Color.FromArgb(255, i, i, i);
    b.Palette = ncp;

    var BoundsRect = new Rectangle(0, 0, Width, Height);
    BitmapData bmpData = b.LockBits(BoundsRect,
                                    ImageLockMode.WriteOnly,
                                    b.PixelFormat);

    IntPtr ptr = bmpData.Scan0;

    int bytes = bmpData.Stride*b.Height;
    var rgbValues = new byte[bytes];

    // fill in rgbValues, e.g. with a for loop over an input array

    Marshal.Copy(rgbValues, 0, ptr, bytes);
    b.UnlockBits(bmpData);
    return b;

Con respecto a su pregunta 4: El ImageLockMode.UserInputBuffer puede darle el control del proceso de asignación de esa gran cantidad de memoria que podría referenciarse en un BitmapData objeto.

Si elige crear usted mismo el BitmapData objeto que puedes evitar un Marshall.Copy . Entonces tendrás que usar esta bandera en combinación con otra ImageLockMode .

Aquí hay un ejemplo que obtendría de una sola vez el contenido del búfer de 24 bbp en un mapa de bits y luego, uno tras otro, lo vuelve a leer en otro búfer en 48 bbp.

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 48bpp
BitmapData 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 reference
bitmap.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 want
GCHandle 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 buff
BitmapData 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...

Si usa ILSpy, verá que este método se vincula a GDI+ y esas ayudas de métodos son más completas.

Entonces podrá volverse loco, por ejemplo, asignando enormes scan0 mapeado en memoria virtual y blitarlo de manera bastante eficiente. Tenga en cuenta que fijar una matriz enorme (y especialmente unas pocas) no será una carga para el GC y le permitirá manipular el byte/corto de una manera totalmente segura (o insegura si no velocidad de búsqueda)


No estoy seguro de si hay una razón por la que lo haces de la forma en que lo haces. Tal vez lo haya. Parece que está lo suficientemente fuera de lo común como para intentar hacer algo más avanzado de lo que implica el título de su pregunta...

Sin embargo, la forma tradicional de crear un mapa de bits a partir de una matriz de bytes es:

using (MemoryStream stream = new MemoryStream(byteArray))
{
     Bitmap bmp = new Bitmap(stream);
     // use bmp here....
}