【问题标题】:How can I overwrite a System.Drawing.Bitmap onto an existing GDI bitmap?如何将 System.Drawing.Bitmap 覆盖到现有的 GDI 位图上?
【发布时间】:2011-02-07 17:25:11
【问题描述】:

如果我有一个.Net Bitmap,我可以通过调用BitmapGetHbitmap() 方法从它创建一个GDI 位图。

Bitmap bmp = new Bitmap(100, 100);
IntPtr gdiBmp = bmp.GetHbitmap();

这很好用,但是每次调用GetHbitmap 时,Windows 都必须分配返回的IntPtr 引用的新内存。

如果可能的话,我想做的是编写一个函数(我知道这里需要 PInvoke),它还会生成 Bitmap 的 GDI 位图副本,但这会覆盖已经引用的现有内存块通过从GetHbitmap 返回的IntPtr,而不是分配新内存。所以它看起来像这样(如果它是Bitmap 的扩展方法):

// desired method signature:
void OverwriteHbitmap(IntPtr gdi)
{

}

// ex:
Bitmap bmp1 = new Bitmap(100, 100);
IntPtr gdi1 = bmp1.GetHbitmap();

Bitmap bmp2 = new Bitmap(100, 100);
bmp2.OverwriteHbitmap(gdi1); // gdi1 is still pointing to the same block
    // of memory, which now contains the pixel data from bmp2

我该怎么做?我假设我需要知道 GDI 位图的结构,并且可能我可以使用 LockBitsBitmapData,但我不确定具体如何。

赏金猎人的线索:

Bitmap 有一个方法LockBits 将位图锁定在内存中并返回一个BitmapData 对象。 BitmapData 对象有一个 Scan0 属性,它是一个 IntPtr 指向锁定位图像素数据的开始(即它指向位图的标题,也就是位图本身)。

我很确定解决方案看起来像这样:

Bitmap bmp1 = new Bitmap(100, 100);
IntPtr gdi1 = bmp1.GetHbitmap(); // now we have a pointer to a 
    // 100x100 GDI bitmap

Bitmap bmp2 = new Bitmap(100, 100);
BitmapData data = bmp2.LockBits();
IntPtr gdi1Data = gdi1 + 68; // magic number = whatever the size 
    // of a GDI bitmap header is
CopyMemory(data.Scan0, gdi1Data, 40000);

解决方案不必是通用的 - 它只需要适用于像素格式 Format32bppArgb(默认 GDI+ 格式)的位图。

【问题讨论】:

  • 到底谁会否决这个问题?

标签: c# .net gdi+ gdi


【解决方案1】:

我认为您回答了自己的问题:使用 LockBits。

How to: Use LockBitsBob Powell - Locking Bits.

我使用这种方法在我正在编写的有趣的 .NET(1.1!)程序中快速绘制分形。在我尝试过的所有方法中,这是迄今为止最快的。

【讨论】:

  • 嗯,我的基本问题是 BitmapData.Scan0 成员指向 GDI+ 位图中位图数据的开始,而来自 Bitmap.GetHbitmap 的 IntPtr 指向整个 GDI 位图结构的开始(即它指向header)。我只需要知道如何计算从标头到数据开头的偏移量。
  • Scan0 和 GetHbitmap 中的 IntPtr 都是可以传递给 RtlMoveMemory(又名 CopyMemory)的指针,所以这部分并不难。
【解决方案2】:

使用 Graphics.FromHdc 从 IntPtr 创建一个 Graphics,并使用 Graphics.DrawImage 将位图粘贴到其中。

【讨论】:

  • 好吧,我觉得自己很愚蠢——这是一种简单而明显(回想起来)的方法。这比这多一点工作,因为 IntPtr 是位图句柄而不是设备上下文,因此必须使用SelectObject 将其选入 DC。至少它仍然需要一个 little PInvoke 来让我开心。 :)
【解决方案3】:

您不应假定 HANDLE 或 HBITMAP 是指向任何内容的指针。它可以实现为句柄表的索引、哈希表的键等。

但是,如果您调用 GDI GetObject 函数,则生成的 BITMAP structure 包含指向实际位的指针。

不确定它是否会那么快(我认为在源和目标的像素格式相同的情况下它会),但SetDIBits 应该完全符合您的要求(替换现有 HBITMAP 中的数据)。

【讨论】:

  • 如果我知道如何从 C# 中使用它,SetDIBits 会完全满足我的需要,但我不知道。 :)
  • Bitmap.GetHbitmap 实际上非常快,所以如果Bitmap.OverwriteHbitmap 更慢,我一点也不介意。我正在努力避免因高频调用 GetHbitmap 而造成的内存流失。
  • 实际上,我猜 GetObject 和 BITMAP 结构看起来正是我想要的。我将使用 GetObject 获取指向包含指向 GDI 位图数据的指针的结构的指针,并将其用作 CopyMemory 中的目标,以及我的位图数据的 Scan0 作为源?
  • 是的。或者调用 SetDIBits 并将 Scan0 作为最后一个参数传递,它将验证格式是否相同,然后为您执行 CopyMemory。如需从 C# 调用这些函数的帮助,请在 pinvoke.net 网站上搜索它们
  • 伙计,你想要 100 只是为了告诉我去 pinvoke.net?我知道我应该让赏金更大。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
  • 2010-12-05
  • 1970-01-01
相关资源
最近更新 更多