Я зашел в тупик со своей программой. У меня в памяти есть простой массив, состоящий из значений RGB растрового изображения DIB (без BITMAPFILEHEADER
). Этот массив создается на C++, но я пытаюсь отобразить его в VB.NET. Я не хочу использовать GDI+, потому что мне нужна чистая скорость.
Это мой код (изображение в файле не имеет заголовка, ширина: 1920 и высота: 100,24 бит, общий размер 6220804):
Dim bData As Byte()
Dim br As BinaryReader = New BinaryReader(File.OpenRead("img1.bmp"))
bData = br.ReadBytes(br.BaseStream.Length) 'no headers just raw data
Dim g As Graphics = Me.CreateGraphics() 'System.Drawing.Graphics.FromImage(bmp) 'or PictureBox1.CreateGraphics()
Dim hdc As IntPtr = g.GetHdc()
Dim bmi As New BITMAPINFO
bmi.bmiheader = New BITMAPINFOHEADER
'Now we fill up the bmi (Bitmap information variable) with all the necessary data
bmi.bmiheader.biSize = 40 'Size, in bytes, of the header (always 40)
bmi.bmiheader.biPlanes = 1 'Number of planes (always one)
bmi.bmiheader.biBitCount = 24 'Bits per pixel (always 24 for image processing)
bmi.bmiheader.biCompression = 0 'Compression: none or RLE (always zero)
bmi.bmiheader.biWidth = 1920
bmi.bmiheader.biHeight = 100
bmi.bmiheader.biSizeImage = 6220804
Dim memHDC As IntPtr = CreateCompatibleDC(hdc)
StretchDIBits(memHDC, 0, 0, 1920, 100, 0, 0, 1920, 100, bData, bmi, 0, 13369376) ' Copy RGB values on an intermediary HDC
BitBlt(hdc, 0, 0, 1920, 100, memHDC, 0, 0, 13369376) 'Print directly from the memHDC
Вот мои структуры:
<StructLayout(LayoutKind.Sequential)>
Structure RGBQUAD
Public rgbBlue As Byte
Public rgbGreen As Byte
Public rgbRed As Byte
Public rgbReserved As Byte
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Class BITMAPINFOHEADER
Public biSize As Int32
Public biWidth As Int32
Public biHeight As Int32
Public biPlanes As Int16
Public biBitCount As Int16
Public biCompression As Int32
Public biSizeImage As Int32
Public biXPelsPerMeter As Int32
Public biYPelsPerMeter As Int32
Public biClrUsed As Int32
Public biClrImportant As Int32
End Class
<StructLayout(LayoutKind.Sequential)>
Private Structure BITMAPINFO
Dim bmiheader As BITMAPINFOHEADER
Dim bmiColors As RGBQUAD
End Structure
Я протестировал почти все возможные комбинации переменных, HDC и графики. Ничего не работает! Где я терплю неудачу?
ПРИМЕЧАНИЕ: кажется, что StretchDIBits и BitBlt выполнены успешно.
Я не использую bmp. Это juat для teata. Я пытался подключить к нему hdc, затем использовать битблт на этом hdc, а затем использовать bmp в picturebox1.
Просто нет права на ошибку
У вас есть много жестко закодированных свойств изображения (ширина, высота и т. д.). Вы уверены, что эти значения верны для массива данных? Вы также уверены, что массив байтов не начинается с заголовка DIB? Кажется странным, что вы извлекаете такие данные без какого-либо способа определить, что они представляют. Если вы уверены в данных, пробовали ли вы использовать метод типа Lockbits (или в худшем случае Bitmap.SetPixel) для записи данных в растровое изображение, чтобы определить, можете ли вы создать допустимое растровое изображение? Как только у вас будет работать известный метод, попробуйте StretchDIBits и BitBlt.
Это всего лишь тестовый сценарий. Позже я включу BITMAPINFOHEADER внутрь файла или массива. Я не хочу использовать SetPixel, потому что он очень медленный. Сейчас я повторно проверяю данные, чтобы убедиться, что они не повреждены. Также при создании этого массива я использовал положительное значение высоты в структуре. Я не знаю, влияет ли это на код vb.net.
Но помимо массива логика моего кода верна? Это должно было сработать?
И даже если массив был поврежден, разве этот код не должен был показывать хотя бы черный прямоугольник?
Я нашел решение. Я думаю, проблема возникает из-за того, что CreateCompatibleDC создает сетку один пиксель за одним пикселем.
Из-за этого ограничения я просто использовал StretchDIBits
на HDC окна изображения:
Dim bData As Byte()
Dim br As BinaryReader = New BinaryReader(File.OpenRead("img1_arr.bmp"))
bData = br.ReadBytes(br.BaseStream.Length)
Dim g As Graphics = PictureBox1.CreateGraphics() 'or Me.CreateGraphics()
Dim dsthdc As IntPtr = g.GetHdc()
Dim bmi As New BITMAPINFO
bmi.bmiheader = New BITMAPINFOHEADER
'Now we fill up the bmi (Bitmap information variable) with all the necessary data
bmi.bmiheader.biSize = 40 'Size, in bytes, of the header (always 40)
bmi.bmiheader.biPlanes = 1 'Number of planes (always one)
bmi.bmiheader.biBitCount = 24 'Bits per pixel (always 24 for image processing)
bmi.bmiheader.biCompression = 0 'Compression: none or RLE (always zero)
bmi.bmiheader.biWidth = 1920
bmi.bmiheader.biHeight = 1080
bmi.bmiheader.biSizeImage = 6220804
StretchDIBits(dsthdc, 0, 0, 1920, 1080, 0, 0, 1920, 1080, bData, bmi, 0, SRCCOPY)
Конечно, в примере используются фиксированные значения только для целей тестирования. Он работает безупречно.
Вам просто нужен System.Runtime.InteropServices.GCHandle
для выделения массива байтов: Dim gcHandle = GCHandle.Alloc(bData, GCHandleType.Pinned)
. Затем наведите указатель на scan0
: Dim pointer = New IntPtr(CLng(gcHandle.AddrOfPinnedObject()))
. Теперь вы можете создать новый Bitmap, используя параметры, которые у вас есть: Dim bitmap = New Bitmap(1920, 100, 1920 * 4, PixelFormat.Format32bppArgb, pointer))
. Конечно gcHandle.Free()
после.
Я не хочу использовать GDI+. После того, как я нарисую на HDC, я планирую соединить несколько изображений вместе, а затем отобразить результат. Если я использую класс Bitmap, мне нужно работать на уровне GDI+, который для меня слишком медленный. Я мог бы также создать простой BITMAPFILEHEADER и просто использовать трюк с потоком памяти для создания растрового изображения. Но спасибо за информацию.
В .Net методы GDI+ работают как BitBlt
и StretchBlt
. Если вам нужна скорость, вам нужна внешняя dll C++, которая выполняет все задачи. Или ДиректХ. Если вы не хотите использовать DIB в VB.Net, вы вернетесь к GDI+. Плюс необходимые преобразования.
Я попробовал сшивание с помощью GDI+: g.DrawImage. Это худшая функция, с которой я когда-либо сталкивался с точки зрения скорости.
Это зависит от того, как вы его используете. Я видел много вредных привычек. SharpDX был бы хорошим выбором (ну, C#, но...).
Куда вы присваиваете данные
bmp
?