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+ 方法的速度与 BitBltStretchBlt 一样快。如果您想要速度,您需要一个执行所有任务的外部 C++ dll。或 DirectX。除非您想在 VB.Net 中使用 DIB,否则您将回到 GDI+。加上所需的转化次数。 我尝试使用 GDI+ 进行拼接: g.DrawImage 。就速度而言,这是我遇到过的最糟糕的功能 这取决于你如何使用它。我见过很多坏习惯。 SharpDX 将是一个不错的选择(嗯,C#,但是...)。

以上是关于VB.NET 在图片框中绘制与设备无关的位图的主要内容,如果未能解决你的问题,请参考以下文章

图片框覆盖 vb.net

GDI+ 支持的图片文件格式

设备无关位图字节数组到图像:参数无效

关于WPF的2000件事 05--矢量图形和呈现与分辨率无关

C++ MFC:将位图绘制到 CFrame 中

BMP文件格式