在原生 C++ 和 C# 之间传递 Graphics 对象
我目前正在开发一个类似于 Paint .NET 的应用程序。我有多种类型的层,它们是用 C# 实现的。这些图层被绘制到由 WinForms 用户控件提供的 .NET Graphics 对象中 - 它类似于 WPF 画布控件。layer 基类有一个 Draw 方法,实现如下:
public void Draw(IntPtr hdc)
{
using (var graphics = Graphics.FromInternalHDC(hdc)
{
// First: Setup rendering settings like SmoothingMode, TextRenderingHint, ...
// Layer specific drawing code goes here...
}
}
对于性能和反编译问题,我正在混合模式程序集中进行图层组合,因为我还应用了斜角或阴影等效果。包装器,当然是用 C++/CLI 编写的,直接从画布控件调用,并将每个层的元数据和目标图形对象(来自我的 C# 编写的画布用户控件的图形对象)转发到本机 C++ 类。
C++/CLI 包装器:
public ref class RendererWrapper
{
public:
void Render(IEnumerable<Layer^>^ layersToDraw, Graphics^ targetGraphics)
{
// 1) For each layer get metadata (position, size AND Draw delegate)
// 2) Send layer metadata to native renderer
// 3) Call native renderer Render(targetGraphics.GetHDC()) method
// 4) Release targetGraphics HDC
};
}
本机 C++ 渲染器:
class NativeRenderer
{
void NativeRenderer::Render(vector<LayerMetaData> metaDataVector, HDC targetGraphicsHDC)
{
Graphics graphics(targetGraphicsHDC);
// Setup rendering settings (SmoothingMode, TextRenderingHint, ...)
for each metaData in metaDataVector
{
// Create bitmap and graphics for current layer
Bitmap* layerBitmap = new Bitmap(metaData.Width, metaData.Height, Format32bppArgb);
Graphics* layerGraphics = new Graphics(layerBitmap);
// Now the interesting interop part
// Get HDC from layerGraphics
HDC lgHDC = layerGraphics->GetHDC();
// Call metaData.Delegate and pass the layerGraphics HDC to C#
// By this call we are ending up in the Draw method of the C# Layer object
metaData.layerDrawDelegate(lgHDC);
// Releasing HDC - leaving interop...
layerGraphics->ReleaseHDC(lgHDC);
// Apply bevel/shadow effects
// Do some other fancy stuff
graphics.DrawImage(layerBitmap, metaData.X, metaData.Y, metaData.Width, metaData.Height);
}
}
}
到现在为止还挺好。上面的代码几乎可以按预期工作,但是......
问题
唯一的问题是,例如,在渲染带有阴影的 PNG 时,我当前的实现缺乏抗锯齿和半透明。所以我只有 2 个 Alpha 通道的值:255 的透明或完全可见的颜色。这种副作用使得使用 Alpha 通道和字体绘制 PNG 看起来非常难看。当我使用纯 C# 代码时,我无法再像以前那样获得同样平滑和漂亮的半透明抗锯齿。
但是:直接在原生 Graphics 对象中绘制字符串时,
layerGraphics->DrawString(...);
抗锯齿和半透明功能回归了。所以这个问题只有在将 Graphics HDC 传递给 .NET 时才很明显。
问题
这个问题有什么解决方案/解决方法吗?我尝试直接在 C# Layer 类中创建位图,并将 HBITMAP 的 IntPtr 返回到本机代码。这种方法是有效的,但在这种情况下,我遇到了另一个问题,因为我找不到将 HBITMAP 转换为具有 alpha 通道的 GDI+ 位图的完美解决方案(绘制字体时,边缘周围有白色像素噪声)。
感谢您的输入!:)
演示解决方案
附上你会在这里找到一个演示解决方案:Sources
在这个演示解决方案中,我测试了 3 种不同的渲染方法(全部在 NativeRenderer.cpp 中实现),而第一个显示了所描述的问题:
1) RenderViaBitmapFromCSharp() - a)在 C++ 中创建一个新的位图,在 C++ 中创建一个新的 Graphics 对象,通过传递 C++ Graphics 对象调用 C# 绘图代码 HDC -失败
但是: b)直接从 C++ 绘图也可以通过创建的位图工作
2) RenderDirectlyFromCSharp() - 从 C++ 中的 C# Graphics 句柄创建一个新的 Graphics 对象,通过传递 C++ Graphics 对象 HDC 调用 C# 绘图代码 -工程
3) RenderDirectlyFromCPP() - 从 C++ 中的 C# 图形句柄创建一个新的图形对象,直接在 C++ 中绘制文本 -工作