VB.NET 在图片框中绘制与设备无关的位图
Posted
技术标签:
【中文标题】VB.NET 在图片框中绘制与设备无关的位图【英文标题】:VB.NET Draw device independent bitmap in picturebox 【发布时间】:2019-08-26 10:15:36 【问题描述】:我的程序陷入了死胡同。我在内存中有一个由 DIB 位图的 RGB 值组成的简单数组(没有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
?
我不使用 bmp。这是茶的juat。我想得到一个hdc然后在那个hdc上bitblt然后在picturebox1中使用bmp
根本没有犯错的余地
你有很多硬编码的图像属性(宽度、高度等)。您确定这些值对于数据数组是正确的吗?您是否还确定字节数组不以 DIB 标头开头?您会在没有某种方式确定它代表什么的情况下检索此类数据,这似乎很奇怪。如果您确定数据,您是否尝试过使用 Lockbits 类型的方法(或者在最坏的情况下为 Bitmap.SetPixel)将数据写入位图以确定是否可以创建有效的位图图像?一旦你有一个已知的方法工作,然后尝试 StretchDIBits 和 BitBlt。
这只是一个测试场景。稍后我将在文件或数组中包含 BITMAPINFOHEADER。我不想使用 SetPixel 因为它很慢。我现在正在重新测试数据以确保它没有损坏。此外,当创建此数组时,我在结构中使用了高度的正值。我不知道这是否会影响 vb.net 代码。
【参考方案1】:
我找到了解决方案。我认为问题出在 CreateCompatibleDC 创建一个逐个像素网格的事实。
由于这个限制,我只是在图片框的 HDC 上使用了StretchDIBits
:
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()))
。您现在可以使用您拥有的参数创建一个新的位图:Dim bitmap = New Bitmap(1920, 100, 1920 * 4, PixelFormat.Format32bppArgb, pointer))
。当然,gcHandle.Free()
之后。
我不想使用 GDI+ 。在我在 HDC 上绘图后,我计划将多个图像拼接在一起,然后显示结果。如果我使用 Bitmap 类,那么我需要在 GDI+ 级别上工作,这对我来说太慢了。我也可以创建一个简单的 BITMAPFILEHEADER 并使用内存流技巧来创建一个位图。但是感谢您提供的信息。
在 .Net 中,GDI+ 方法的速度与 BitBlt
和 StretchBlt
一样快。如果您想要速度,您需要一个执行所有任务的外部 C++ dll。或 DirectX。除非您想在 VB.Net 中使用 DIB,否则您将回到 GDI+。加上所需的转化次数。
我尝试使用 GDI+ 进行拼接: g.DrawImage 。就速度而言,这是我遇到过的最糟糕的功能
这取决于你如何使用它。我见过很多坏习惯。 SharpDX 将是一个不错的选择(嗯,C#,但是...)。以上是关于VB.NET 在图片框中绘制与设备无关的位图的主要内容,如果未能解决你的问题,请参考以下文章