31

I try to get all byte values from a Bitmap(System.Drawing.Bitmap). Therefore I lock the bytes and copy them:

public static byte[] GetPixels(Bitmap bitmap){
    if(bitmap-PixelFormat.Equals(PixelFormat.Format32.bppArgb)){
        var argbData = new byte[bitmap.Width*bitmap.Height*4];
        var bd = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        System.Runtime.InteropServices.Marshal.Copy(bd.Scan0, argbData, 0, bitmap.Width * bitmap.Height * 4);
        bitmap.UnlockBits(bd);
    }
}

I tested this Image with a very simple 2x2 PNG image with pixels (red, green, blue, white) that I created in Photoshop. Because of the format, I expected the following values within the argbData:

255 255   0   0    255 0   255   0 
255 0     0 255    255 255 255 255 

But I got:

0     0 255 255     0 255   0 255
255   0   0 255   255 255 255 255

But this is a BGRA format. Does anybody know why the bytes seems swapped? By the way, when I use the image directly for a Image.Source as shown below, the Image is shown correctly. So what's my fault?

<Image Source="D:/tmp/test2.png"/>
0xBADF00D
  • 980
  • 1
  • 12
  • 25

3 Answers3

57

Pixel data is ARGB, 1 byte for alpha, 1 for red, 1 for green, 1 for blue. Alpha is the most significant byte, blue is the least significant. On a little-endian machine, like yours and many others, the little end is stored first so the byte order is bb gg rr aa. So 0 0 255 255 equals blue = 0, green = 0, red = 255, alpha = 255. That's red.

This endian-ness order detail disappears when you cast bd.Scan0 to an int* (pointer-to-integer) since integers are stored little-endian as well.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 6
    Excelent! I can only add that you can check the byte order ("endianness") in which data is stored in this computer architecture via the [BitConverter.IsLittleEndian](http://msdn.microsoft.com/en-us/library/system.bitconverter.islittleendian.aspx) field. – DmitryG Nov 12 '11 at 13:02
  • 3
    That's a good point about `BitConverter.IsLittleEndian`, but I think @HansPassant's comment about endian-ness is really valuable - this is another place where the endian-ness should **not** be considered. [Here's](http://commandcenter.blogspot.com.au/2012/04/byte-order-fallacy.html) a great article about this topic.. – Jonno Aug 06 '15 at 12:07
3

In Bpp32Argb pixel format. You dont' need to byte-by-byte access.

Trun Scan0 to an Int32 pointer in unsafe context.

unsafe
{
    var ptr=(int*)bmData.Scan0;
}

You can do some bit operation like below To access color channels of first pixel.

And dont need to care byte-order.

var a=(ptr[0] & 0xFF000000)>>24;
var r=(ptr[0] & 0x00FF0000)>>16;
var g=(ptr[0] & 0x0000FF00)>>8;
var b=(ptr[0] & 0x000000FF);

BTW you can work with Color.ToArgb() returned int easily.

IlPADlI
  • 1,943
  • 18
  • 22
  • 3
    The accepted answer already mention this, but unfortunately you can't use unsafe code in some circumstances. For instances, I worked for several companies that doesn't allow unsafe code at all. – 0xBADF00D Jan 11 '17 at 07:09
  • 2
    I'm just glad you mentioned the ToArgb() - saved me quite a bit of time. :) – Eric Nov 10 '18 at 02:58
1

AFAIK it is technically based on COLORREF (which is used in Windows GDI/GDI+ everywhere) and that is stored RGBA in memory... see http://msdn.microsoft.com/en-us/library/dd183449%28VS.85%29.aspx

Yahia
  • 69,653
  • 9
  • 115
  • 144
  • if I understand the page, that means every time I ready RGB, that is not real byteorder. It is always means BGR? Or is there a kind of flag indicating this? – 0xBADF00D Nov 12 '11 at 12:50