Этот вопрос о том, как читать/записывать, распределять и управлять данными пикселей растрового изображения.
Вот пример того, как выделить байтовый массив (управляемую память) для данных пикселей и создать растровое изображение, используя его:
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;
}
Я думал, что Bitmap сделает копию данных массива, но на самом деле он указывает на те же данные. Вы можете увидеть:
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 [] (управляемая память) и освобождать() GCHandle? Если это небезопасно, мне нужно сохранить закрепленный массив. Насколько это плохо для GC/Performance?
Безопасно ли изменять данные (например, data [0] = 255;)?
Адрес Scan0 может быть изменен с помощью GC? Я имею в виду, я получаю Scan0 из заблокированного растрового изображения, затем разблокирую его и через некоторое время снова блокирую, Scan0 может отличаться?
Какова цель ImageLockMode.UserInputBuffer в методе LockBits? Найти информацию об этом очень сложно! MSDN не объясняет это ясно!
ОБНОВЛЕНИЕ 1: некоторые последующие
Вы должны держать это закрепленным. Это замедлит GC? Я спросил это здесь. Это зависит от количества изображений и его размеров. Никто не дал мне количественного ответа. Это кажется, что это трудно определить. Вы также можете выделить память с помощью Marshal или использовать неуправляемую память, выделенную растровым изображением.
Я сделал много испытаний с использованием двух потоков. Пока растр заблокирован, все в порядке. Если битмап разблокирован, то это небезопасно! Мой связанный пост о чтении/записи непосредственно в Scan0. Boing answer "Я уже объяснил выше, почему вам повезло, что вы можете использовать scan0 вне блокировки. Потому что вы используете оригинальный bmp PixelFormat и этот GDI оптимизирован в этом случае, чтобы дать вам указатель, а не копию. Этот указатель действителен пока ОС не примет решение освободить ее. Единственный раз, когда есть гарантия - между LockBits и UnLockBits. Период. "
Да, это может случиться, но GC обрабатывает большие области памяти по-разному, он реже перемещает/освобождает этот большой объект. Таким образом, это может занять некоторое время, чтобы GC переместил этот массив. Из MSDN: "Любое выделение, большее или равное
85,000 bytes
, идет наlarge object heap (LOH)
"..."LOH собирается только во время сбора данных поколения 2". В .NET 4.5 есть улучшения в LOH.На этот вопрос ответил @Boing. Но я собираюсь признать. Я не до конца понял это. Поэтому, если бы
Boing
или кто-то еще могplease clarify it
, я был бы рад. Кстати, почему я не могу просто напрямую читать/писать в Sca0 без блокировки? => Вы не должны писать напрямую в Scan0, потому что Scan0 указывает на копию данных точечного рисунка, созданную неуправляемой памятью (внутри GDI). После разблокировки эта память может быть перераспределена для других вещей, больше не уверен, что Scan0 будет указывать на фактические данные Bitmap. Это может быть воспроизведено с получением Scan0 в блокировке, разблокировке и выполнении некоторого поворота-переворота в разблокированном растровом изображении. Через некоторое время Scan0 укажет на недопустимую область, и вы получите исключение при попытке чтения/записи в ее ячейку памяти.