PixelFormat.Format32bppArgb, похоже, имеет неправильный порядок байтов
Я пытаюсь получить все значения байтов из Bitmap (System.Drawing.Bitmap). Поэтому я блокирую байты и копирую их:
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);
}
}
Я тестировал это изображение с очень простым изображением 2x2 PNG с пикселями (красный, зеленый, синий, белый), которые я создал в Photoshop. Из-за формата я ожидал следующие значения в argbData:
255 255 0 0 255 0 255 0
255 0 0 255 255 255 255 255
Но я получил:
0 0 255 255 0 255 0 255
255 0 0 255 255 255 255 255
Но это формат BGRA. Кто-нибудь знает, почему байты заменяются?
Кстати, когда я использую изображение непосредственно для Image.Source, как показано ниже, изображение отображается правильно. Так в чем моя вина?
<Image Source="D:/tmp/test2.png"/>
Ответы
Ответ 1
Пиксельные данные: ARGB, 1 байт для альфа, 1 для красного, 1 для зеленого, 1 для синего. Альфа - самый значительный байт, синий - наименее значимый. На маленькой машине, похожей на вашу и многие другие, маленький конец сохраняется первым, поэтому порядок байтов - bb gg rr aa. Таким образом, 0 0 255 255 равно синему = 0, зеленый = 0, красный = 255, альфа = 255. Это красный цвет.
Эта спецификация порядка endian-ness исчезает, когда вы накладываете bd.Scan0 на int * (указатель на целое число), так как целые числа также хранятся в маленьком конце.
Ответ 2
AFAIK технически основан на COLORREF
(который используется в Windows GDI/GDI + везде) и который хранится в памяти RGBA... см. http://msdn.microsoft.com/en-us/library/dd183449%28VS.85%29.aspx
Ответ 3
В формате пикселя Bpp32Argb. Вам не нужно байт-байтовый доступ.
Trun Scan0 в указатель Int32 в небезопасном контексте.
unsafe
{
var ptr=(int*)bmData.Scan0;
}
Вы можете выполнить некоторую операцию бит, как показано ниже. Для доступа к цветным каналам первого пикселя.
И не нужно заботиться о байтовом порядке.
var a=(ptr[0] & 0xFF000000)>>24;
var r=(ptr[0] & 0x00FF0000)>>16;
var g=(ptr[0] & 0x0000FF00)>>8;
var b=(ptr[0] & 0x000000FF);
Кстати, вы можете легко работать с Color.ToArgb()
возвращенным int
.