【问题标题】:Converting a BITMAP structure to a BitmapImage/BitmapSource compatible byte array?将 BITMAP 结构转换为与 BitmapImage/BitmapSource 兼容的字节数组?
【发布时间】:2015-05-06 05:32:30
【问题描述】:

我有一个包含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 也可以。

【问题讨论】:

  • 您确定要使用 BitmapImage 吗?还是 BitmapSource?
  • 是的。我需要在 WPF 应用程序中显示它。
  • 是的,没错。大多数 WPF 应用程序不需要 BitmapImage。例如,Image 控件(在 XAML 中使用)不需要 BitmapImage,它只需要一个 ImageSource(它是基类,BitmapSource 派生自它,BitmapImage 派生自 BitmapSource)。因此我的问题。你最后想做什么?
  • 啊哈。那么 BitmapSource 也可以。
  • 我认为不需要任何代码。与其帮助我们处理这个有严重缺陷的代码,不如解决真正的问题。那么,真正的问题是什么?你的实际目标是什么。

标签: c# bitmap pinvoke


【解决方案1】:

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

bitmap.WidthBytes * bitmap.Height

这不是 BitmapImage 的预期来源,因为 BitmapImage 来源需要是完整的位图文件结构,其中包括像素数据之前的 54 个字节。

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

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

【讨论】:

    【解决方案2】:

    如果您只需要 BitmapSource(或 ImageSource),则可以使用 GDI 的 CreateBitmapIndirect function 和 WPF 互操作的 Imaging.CreateBitmapSourceFromHBitmap Method ,如下所示:

      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 到字节数组,然后再返回到 BitmapSource。这样做的原因是我有一个独立于平台的核心,并将图标存储为字节。所以当我存储图标时,它是一个字节数组,当我再次读取它(从 UI 层)时,它需要转换为BitmapSource。这有意义吗?
    • 我不明白。从 BITMAP 到字节数组再到 BitmapSource 是什么意思?位图“位”不足以创建 BitmapSource(或实际上任何其他位图格式)。
    • 所以为了从BITMAP 转换为字节数组,我首先需要将其转换为BitmapSource,然后再转换为字节数组?
    • “将 BITMAP 转换为字节数组”是什么意思?这个字节数组应该包含什么?
    • 图像本身,如问题中所述。并且此图像(字节数组格式)必须能够转换回BitmapSource
    【解决方案3】:

    这里出了点问题。 BITMAP 不是一个对象,根据 GetObject API 文档,声称获取带有像素的字节数组的代码是不正确的:

    如果 hgdiobj 是通过调用 CreateDIBSection 创建的位图的句柄,并且指定的缓冲区足够大,则 GetObject 函数返回一个 DIBSECTION 结构。此外,包含在 DIBSECTION 中的 BITMAP 结构的 bmBits 成员将包含指向位图位值的指针。如果 hgdiobj 是通过任何其他方式创建的位图的句柄,则 GetObject 仅返回位图的宽度、高度和颜色格式信息。您可以通过调用GetDIBits或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));
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-20
      • 2015-07-26
      • 1970-01-01
      • 2016-07-06
      • 1970-01-01
      • 2011-09-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多