Скопируйте одно растровое изображение в большее растровое изображение, используя без использования Graphics.DrawImage - PullRequest
0 голосов
/ 18 марта 2009

Это продолжение Рендеринг в один растровый объект из нескольких потоков

То, чего я пытаюсь добиться, - это взять растровое изображение, скажем, 50x50 пикселей и нарисовать его на более крупном растровом изображении (100x100 пикселей) в любой точке большего изображения, используя функцию BitBap LockBits или любую другую, но НЕ графику. Мои причины отказа от использования DrawImage изложены в другой теме.

Мне удалось получить что-то, используя Marshal.Copy из исходного BitmapData в dest BitmapData, но это создавало мозаичное растянутое изображение по горизонтали.

Ответы [ 3 ]

4 голосов
/ 18 марта 2009

Вы можете манипулировать изображением в памяти, не полагаясь на системные вызовы. Если вы покопаетесь в базовом формате файла .BMP, вы можете создать свой собственный класс независимой от устройства растровой карты, который действительно «понимает» низкоуровневый формат .BMP.

Например, изображение размером 8 бит на пиксель, по сути, представляет собой двумерный массив байтов (каждый байт равен 1 пикселю) плюс простая таблица цветов. Грубо говоря (и это очень, очень грубо):

byte[,] bColors = new byte[3,256]; // 256 RGB colors
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels 

Хитрость заключается в том, чтобы (как всегда) получить необработанные данные о пикселях, выполнить обработку и затем обновить необработанные данные о пикселях с вашими изменениями.

Раньше я подходил к этому, преобразовывая HBITMAP GDI в DIB 24bpp, выполняя свою классную обработку изображений на необработанных пикселях (3 байта на пиксель, это облегчает), затем преобразовывая DIB обратно в HBITMAP. Все это было с использованием только классического GDI (даже до GDI +, не говоря уже о C #).

Используя этот подход, вы могли бы разработать управляющую структуру, позволяющую нескольким авторам использовать разные разделы вашего гораздо большего изображения.

Однако ... низкоуровневые вызовы BitBlt GDI, вероятно, будут намного эффективнее, чем все, что вы можете сделать. Если бы я был тобой, я бы был уверен, что выполнение 50 или 100 бит-битов подряд будет слишком медленным (тебе, вероятно, придется делать это в c ++).

Наиболее раздражающими проблемами при работе с DIB являются:

  1. Преобразование DIB в фактическое «изображение», готовое для отображения, и
  2. Преобразование фактического «изображения» в DIB
  3. Сохранение DIB как что-то отличное от .BMP

Основные ссылки, когда я начал изучать «ужас», которым на самом деле являются изображения:

Как вы добираетесь до / из .NET Image ... ну ... это хороший вопрос:)

1 голос
/ 20 мая 2009

Это должно работать нормально, используя LockBits / BitmapData, если вы используете 32-битный формат пикселей [P] ARGB. Хитрость заключается в том, что вам придется копировать данные по одной строке за раз, чтобы они совпали в правильных местах. Вы должны быть в состоянии сделать это, используя что-то вроде:

Rectangle srcArea = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, destBitmap.PixelFormat);
Rectangle destArea = new Rectangle(25, 25, srcBitmap.Width, srcBitmap.Height);
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, destBitmap.PixelFormat);

IntPtr srcPtr = srcData.Scan0;
IntPtr destPtr = destData.Scan0;
byte[] buffer = new byte[srcData.Stride];
for (int i = 0; i < srcData.Height; ++i)
{
    Marshal.Copy(srcPtr, buffer, 0, buffer.Length);
    Marshal.Copy(buffer, 0, destPtr, buffer.Length);

    srcPtr += srcData.Stride;
    destPtr += destData.Stride;
}

srcBitmap.UnlockBits(srcData);
destBitmap.UnlockBits(destData);

Как предупреждение, этот код не будет работать как есть, потому что я не уверен, каковы правильные заклинания для увеличения IntPtr. Я делал то же самое раньше, но в C ++. Кроме того, я не знаю, есть ли способ напрямую скопировать данные вместо использования промежуточного буфера.

Дополнительное предупреждение: LockBits вызывает srcBitmap и размер буфера предполагает, что srcBitmap будет полностью заключен в destBitmap. Если это не так (некоторая часть растрового изображения будет обрезана), область заблокирована, и размер буфера необходимо будет отрегулировать.

Если вы не используете 32-пиксельный формат пикселей (то есть 24 бит / дюйм), это будет сложнее. Ход вашего исходного BitmapData может включать некоторое количество отступов, которые не следует копировать. Вы можете обойти это, рассчитав количество фактических данных пикселей в исходной строке, и скопировать это количество. Индексированные форматы пикселей будут еще более полезными.

0 голосов
/ 18 марта 2009

Я бы порекомендовал взглянуть на структуру внутренней растровой памяти.

Лучшим подходом, я думаю, было бы не пытаться установить BitmapData напрямую. Вместо этого я бы сделал один общий байтовый массив соответствующего размера и установил байтовый массив непосредственно из ваших небольших изображений.

Как только вы создадите свое большое изображение, вы можете взять окончательный байтовый массив и непосредственно создать битовую карту из байтовых данных.

Это имеет то преимущество, что позволяет вам контролировать управление памятью, выполнять операции и т. Д., Как вы, казалось, хотели сделать в своем первоначальном посте. Он также должен быть очень быстрым для доступа к данным.

...