将位图结构转换为位图/位图兼容字节数组



i具有一个包含BITMAP结构的变量。该结构定义如下。

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct BITMAP
{
    public Int32 Type;
    public Int32 Width;
    public Int32 Height;
    public Int32 WidthBytes;
    public UInt16 Planes;
    public UInt16 BitsPixel;
    public IntPtr Bits;
}

这种结构没有错。当我尝试使用GDI GetObject API获取位图时,它会带有真实数据。

当我尝试将其转换为字节数组并再次返回时,问题出现了。

在下面,您会看到如何将结构转换为字节数组。再次工作正常,我在数组中看到数据。

var bitmap = new BITMAP();
var bufferSize = Marshal.SizeOf(bitmap);
GetObject(bitmapHandle, bufferSize, out bitmap);
var bytes = new byte[bitmap.WidthBytes * bitmap.Height];
Marshal.Copy(bitmap.Bits, bytes, 0, bytes.Length);
DeleteObject(bitmapHandle);
//"bytes" now contains the byte array of pixels.

现在,这一切都出现了。稍后,当我尝试将此给定的数组转换回BitmapImage时,我会得到NotSupportedException

这是我转换的代码。

var image = new BitmapImage();
using (var stream = new MemoryStream(bytes))
{
    stream.Position = 0;
    image.BeginInit();
    image.CreateOptions = BitmapCreateOptions.None;
    image.CacheOption = BitmapCacheOption.OnDemand;
    image.UriSource = null;
    image.StreamSource = stream;
    image.EndInit(); //it throws the exception here
}
image.Freeze();

例外消息是No imaging component suitable to complete this operation was found

我尝试在线寻找解决方案,但没有运气。我似乎无法弄清楚我在做什么错。我认为这是从BITMAP到字节数组的转换过程,但我不知道。

应该注意的是,BitMapsource也可以。

创建位图数据数组时,您将失败。您正在计算数组大小,只是乘以

bitmap.WidthBytes * bitmap.Height

,这不是位映射的预期来源,因为位映射源需要是一个完整的位图文件结构,其中包括Pixel Data之前的54个字节。

有关位图结构的更多信息:https://www.daubnet.com/en/file-format-bmp

也许您还需要知道像素数据区域的大小受"填充"的影响,该字节位于每个扫描线末端的额外字节,添加到完整的INT32块中以加速位图加载。

如果您只需要bitmapsource(或images库),则可以使用GDI的CreateBitMapIndirect函数和WPF InterOp的Interop imaging.createbitebitmapsourcefromhbitmap方法,类似于此:

  static BitmapSource FromBITMAP(ref BITMAP bmp)
  {
        Int32Rect rect = new Int32Rect(0, 0, bmp.Width, bmp.Height);
        IntPtr hbitmap = CreateBitmapIndirect(ref bmp);
        if (hbitmap == IntPtr.Zero)
            return null;
        try
        {
            return Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());
        }
        finally
        {
            DeleteObject(hbitmap);
        }
  }
  [DllImport("gdi32.dll", SetLastError = true)]
  public static extern IntPtr CreateBitmapIndirect(ref BITMAP lpbm);
  [DllImport("gdi32.dll", SetLastError = true)]
  public static extern bool DeleteObject(IntPtr hObject);

这里有问题。Bitmap不是对象,根据GetObject API文档,声称使用像素的字节数组的代码只是不正确的。

如果HGDIOBJ是通过调用Createbsectia的命中图创建的位图的手柄,并且指定的缓冲区足够大,则GetObject函数将返回DibSection结构。此外,DibSection中包含的位图结构的BMBIT成员将包含指向位图位值的指针。如果HGDIOBJ是通过任何其他方式创建的位图的手柄,则GetObject仅返回位图的宽度,高度和颜色格式信息。您可以通过调用getDibit或getbitmapbits函数来获得位图的位值。

所以让我们从头开始。据我所知,您的代码始于所谓的BitMaphandle,我认为这是从某个地方进行的HBITMAP。如果是这样,我认为您真正需要的是一个访问HBITMAP并输出字节数组的功能,而另一个功能则以您描述的方式。这样的东西:

    private static byte[] GetBitmapData(IntPtr hBitmap)
    {
        var source = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, null);
        // You may use Bmp, Jpeg or other encoder of your choice
        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(source));
        var stream = new MemoryStream();
        encoder.Save(stream);
        return stream.ToArray();
    }
    private static BitmapSource GetBitmapSource(byte[] data)
    {
        return BitmapFrame.Create(new MemoryStream(data));
    }

最新更新