Search code examples
c#bitmapmarshalling

Copying bytes from Bitmap to byte array and back with Marshall.Copy doesn't work right


I want to copy bytes with Marshall.Copy. My code work, but bytes is strange for me. I think I got indexes, not real bytes. If this compute and saves back, I got different colors in the image with a lot bigger byte size (image size is the same).

 Bitmap bmp = new Bitmap(imagepath);
    Width = bmp.Width;
    Height = bmp.Height;
    byte[] data;
    BitmapData bdata;
    switch (bmp.PixelFormat)
    {
      case PixelFormat.Format8bppIndexed:
      {
        data = new byte[Width * Height];
        bdata = bmp.LockBits(new Rectangle(0, 0, Width, Height),ImageLockMode.ReadOnly, bmp.PixelFormat);
        Marshal.Copy(bdata.Scan0, data, 0, data.Length);
        bmp.UnlockBits(bdata);
        break;
      }
    }

Save image from bytes:

BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(data, 0, pNative, Width * Height);
bmp.UnlockBits(bmData);
bmp.Save("output.gif",ImageFormat.Gif); //without format, have same problem

If I read color from first pixel, I got: Color [A=0, R=0, G=0, B=2]. Is this really color in the input image?

I don't know, why the output is soo different from the input. Where is the problem?

Example from input and output (sorry for small images): Input Output


Solution

  • You did not show how you created the second bmp for reloading the bytes. But the PixelFormat is 8bbpIndexed, which means that your data array will contain palette indices instead of direct color information. When you create your second bmp with 8 bit pixel format it will use a default palette, which may be different from the original one.

    So you must save the bmp.Palette of the first image, then use it to restore the actual colors of your second bmp instance.

    Update: Though you can set the palette entries one by one, it has no effect. Because bmp.Palette is a property. You must set the whole palette instead. Additionally, here is a post with indexed bitmap manipulation (see the ConvertPixelFormat) method.