1

I reached a dead end with my program. I have in memory a simple array consisting of RGB values of a DIB bitmap (no BITMAPFILEHEADER). This array is generated in C++ but I try to display it in VB.NET . I do not want to use GDI+ because I need raw speed.

This is my code (the image in the file has no header, width:1920 and height:100,24 bits,total size 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

Here are my structures:

<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

I tested almost every possible combination of variables,HDCs and graphics. Nothing works! Where am I failling?

NOTE: StretchDIBits and BitBlt seems to succed

sergiu reznicencu
  • 1,039
  • 1
  • 11
  • 31
  • Where are you assigning data to `bmp`? – Brian M Stafford Apr 04 '19 at 15:35
  • I am not uaing bmp. It is juat for teata. I was trting to get a hdc to it then bitblt on that hdc and then use bmp in picturebox1 – sergiu reznicencu Apr 04 '19 at 15:50
  • There's simply no room for mistake – sergiu reznicencu Apr 04 '19 at 16:17
  • You have a lot of hard-coded image properties (width, height, etc.). Are you certain that these values are correct for the data array? Are you also certain that the byte array does not start with a DIB header? It seems strange that you would retrieve such data without some way of determining what it represents. If you are certain about the data, have you tried using a Lockbits type method (or in worst case Bitmap.SetPixel) to write the data to a bitmap to determine if you can create a valid bitmap image? Once you have a known method working, then try StretchDIBits and BitBlt. – TnTinMn Apr 04 '19 at 19:37
  • This is just a test scenario. Later I will include the BITMAPINFOHEADER inside the file or array. I don't want to use SetPixel because is very slow. I am now retesting the data to make sure it is not corrupted. Also when this array was created I used positive value of height in the structure. I don't know if this affects the vb.net code. – sergiu reznicencu Apr 04 '19 at 19:50
  • But besides the array is my code's logic correct? Should have this worked? – sergiu reznicencu Apr 04 '19 at 19:51
  • And even if the array was corrupted , wasn't this code supposed to at least show a black rectangle? – sergiu reznicencu Apr 04 '19 at 20:13

1 Answers1

1

I found a solution. I think the problem arises from the fact that CreateCompatibleDC creates a one pixel by one pixel grid. Because of this limitation I simply used StretchDIBits on the picture box's 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)

Of course the example uses fixed values only for test purposes. It works flawlessly.

sergiu reznicencu
  • 1,039
  • 1
  • 11
  • 31
  • You just need a `System.Runtime.InteropServices.GCHandle` to allocate the byte array: `Dim gcHandle = GCHandle.Alloc(bData, GCHandleType.Pinned)`. Then, get a pointer to `scan0`: `Dim pointer = New IntPtr(CLng(gcHandle.AddrOfPinnedObject()))`. You can now create a new Bitmap using the parameters you have: `Dim bitmap = New Bitmap(1920, 100, 1920 * 4, PixelFormat.Format32bppArgb, pointer))`. Of course, `gcHandle.Free()` after. – Jimi Apr 05 '19 at 00:01
  • I don't want to use GDI+ . After I draw on the HDC I plan to stich multiple images together and then display the result. If I use Bitmap class then I need to work on GDI+ level which is too slow for me. I could have also created a simple BITMAPFILEHEADER and just use the memory stream trick to create a Bitmap. But thanks for the information. – sergiu reznicencu Apr 05 '19 at 12:31
  • In .Net, GDI+ methods are fast as `BitBlt` and `StretchBlt`. If you want speed, you need an external C++ dll that performs all the Tasks. Or DirectX. Unless you want to use the DIB in VB.Net, then you're back to GDI+. Plus the required conversions. – Jimi Apr 05 '19 at 12:36
  • I tried the stiching with GDI+ : g.DrawImage . It is the worst function I ever encountered in terms of speed – sergiu reznicencu Apr 05 '19 at 12:37
  • It depends on how you're using it. I've seen a lot of bad habits. SharpDX would be a good choice (well, C#, but...). – Jimi Apr 05 '19 at 12:40