Как я могу перезаписать System.Drawing.Bitmap на существующее растровое изображение GDI? - PullRequest
1 голос
/ 14 апреля 2010

Если у меня есть .Net Bitmap, я могу создать из него растровое изображение GDI, вызвав GetHbitmap() метод Bitmap.

Bitmap bmp = new Bitmap(100, 100);
IntPtr gdiBmp = bmp.GetHbitmap();

Это работает нормально, но каждый раз, когда вы вызываете GetHbitmap, Windows должна выделять новую память, на которую ссылается возвращаемое IntPtr.

Что я хотел бы сделать - если это возможно - написать функцию (я знаю, что здесь потребуется PInvoke), которая также генерирует растровую копию GDI Bitmap, но она перезаписывает существующий фрагмент памяти, на который уже ссылались IntPtr возвращается из GetHbitmap вместо выделения новой памяти. Так это будет выглядеть примерно так (если бы это был метод расширения Bitmap):

// desired method signature:
void OverwriteHbitmap(IntPtr gdi)
{

}

// ex:
Bitmap bmp1 = new Bitmap(100, 100);
IntPtr gdi1 = bmp1.GetHbitmap();

Bitmap bmp2 = new Bitmap(100, 100);
bmp2.OverwriteHbitmap(gdi1); // gdi1 is still pointing to the same block
    // of memory, which now contains the pixel data from bmp2

Как я могу это сделать? Я предполагаю, что мне нужно будет знать структуру растрового изображения GDI, и, вероятно, я могу использовать LockBits и BitmapData для этого, но я не совсем уверен, как именно.

Подсказки для охотников за головами:

Bitmap имеет метод LockBits, который блокирует растровое изображение в памяти и возвращает объект BitmapData. Объект BitmapData имеет свойство Scan0, которое является IntPtr, указывающим на начало пиксельных данных заблокированного растрового изображения (т. Е. Оно не указывает на заголовок растрового изображения, то есть на начало растрового изображения сам по себе).

Я почти уверен, что решение выглядит примерно так:

Bitmap bmp1 = new Bitmap(100, 100);
IntPtr gdi1 = bmp1.GetHbitmap(); // now we have a pointer to a 
    // 100x100 GDI bitmap

Bitmap bmp2 = new Bitmap(100, 100);
BitmapData data = bmp2.LockBits();
IntPtr gdi1Data = gdi1 + 68; // magic number = whatever the size 
    // of a GDI bitmap header is
CopyMemory(data.Scan0, gdi1Data, 40000);

Решение не должно быть универсальным - оно должно работать только для растровых изображений с форматом пикселей Format32bppArgb (формат GDI + по умолчанию).

Ответы [ 3 ]

3 голосов
/ 14 апреля 2010

Я думаю, что вы ответили на свой вопрос: используйте LockBits.

См http://msdn.microsoft.com/en-us/library/ms229672.aspx а также http://www.bobpowell.net/lockingbits.htm

Я использовал этот метод для быстрого рисования фракталов в программе .NET (1.1!), Которую я писал для забавы. Из всех методов, с которыми я экспериментировал, это был самый быстрый вариант.

2 голосов
/ 29 апреля 2010

Создайте графику из IntPtr с Graphics.FromHdc и используйте Graphics.DrawImage, чтобы вставить в нее растровое изображение.

2 голосов
/ 23 апреля 2010

Вы не должны предполагать, что HANDLE или HBITMAP является указателем на что-либо. Он может быть реализован как индекс в таблице дескрипторов, ключ хеш-таблицы и т. Д.

Однако, если вы вызываете функцию GDI GetObject , результирующая структура BITMAP содержит указатель на действительные биты.

Не уверен, что это будет так же быстро (я думаю, что в случае того же формата пикселей для источника и назначения это будет), но SetDIBits должен делать именно то, что вы хотите (заменить данные в существующий HBITMAP).

...