bar*_*boy 6 c# graphics mixed-mode gdi+ c++-cli
在本机C++和C#之间传递Graphics对象
我目前正在开发类似Paint .NET的应用程序.我有多种类型的层,用C#实现.这些图层被绘制到由WinForms用户控件提供的.NET Graphics对象中 - 它类似于WPF画布控件.图层基类有一个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...
}
}
Run Code Online (Sandbox Code Playgroud)
对于性能和反编译问题,我正在混合模式程序集中进行层的组合,因为我还应用了斜角或投影等效果.包装器,当然是用C++/CLI编写的,直接从canvas控件调用,并将每个层的元数据和目标Graphics对象(来自我的C#编写的canvas用户控件的Graphics对象)转发到本机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
};
}
Run Code Online (Sandbox Code Playgroud)
Native 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.上面的代码几乎按预期工作,但......
问题
唯一的问题是,当我使用阴影渲染PNG时,我当前的实现缺少抗锯齿和半透明度.所以我只有Alpha通道的2个值:255处的透明或完全可见颜色.这个副作用使得绘制带有alpha通道和字体的PNG非常难看.当我使用纯C#代码时,我无法像之前那样获得相同的平滑和漂亮的半透明抗锯齿.
但是:直接在本地Graphics对象中绘制字符串时,
layerGraphics->DrawString(...);
Run Code Online (Sandbox Code Playgroud)
抗锯齿和半透明度恢复良好.所以问题只有在将Graphics HDC传递给.NET时才会明显.
问题
这个问题有解决方案/解决方法吗?我试图直接在C#Layer类中创建Bitmap,并将HBITMAP的IntPtr返回到本机代码.这种方法是有效的,但在这种情况下我有另一个问题,因为我找不到一个完美的解决方案,用于将HBITMAP转换为带有alpha通道的GDI + Bitmap(绘制字体时白色像素噪声围绕边缘).
感谢您的输入!:)
演示解决方案
附上你会在这里找到一个演示解决方案:来源
在这个演示解决方案中,我正在测试3种不同的渲染方法(全部在NativeRenderer.cpp中实现),而FIRST ONE则显示所描述的问题:

1)RenderViaBitmapFromCSharp() - a)在C++中创建一个新的位图,用C++创建一个新的Graphics对象,通过传递C++ Graphics对象调用C#绘图代码HDC - Fails
但是: b)直接从C++绘图也可以通过创建的位图工作
2)RenderDirectlyFromCSharp() -创建新的图形从C对象#图形在C++中处理,通过使C++ Graphics对象HDC调用C#绘图代码- 工程
3)RenderDirectlyFromCPP() - 用C++中的C#Graphics句柄创建一个新的Graphics对象,直接在C++中绘制文本 - Works
我最终用 C# 创建了位图并将对象传递给 C++/CLI。正如 Hans 和 Vincent 已经提到的,你必须避免使用 GetHDC。所以我的解决方法读取伪代码如下:
Layer.cs C#:
public Bitmap Draw()
{
var bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
using (var graphics = Graphics.FromBitmap(bitmap)
{
// First: Setup rendering settings like SmoothingMode, TextRenderingHint, ...
// Layer specific drawing code goes here...
}
return bitmap;
}
Run Code Online (Sandbox Code Playgroud)
NativeRenderer.cs C++:
void NativeRenderer::RenderFromBitmapCSharp(System::Drawing::Bitmap^ bitmap)
{
// Create and lock empty native bitmap
Bitmap *gdiBitmap = new Bitmap(bitmap->Width, bitmap->Height, PixelFormat32bppARGB);
Rect rect(0, 0, bitmap->Width, bitmap->Height);
BitmapData bitmapData;
gdiBitmap->LockBits(&rect, Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData);
// Lock managed bitmap
System::Drawing::Rectangle rectangle(0, 0, bitmap->Width, bitmap->Height);
System::Drawing::Imaging::BitmapData^ pBitmapData = bitmap->LockBits(rectangle, System::Drawing::Imaging::ImageLockMode::ReadOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
// Copy from managed to unmanaged bitmap
::memcpy(bitmapData.Scan0, pBitmapData->Scan0.ToPointer(), bitmap->Width * bitmap->Height * 4);
bitmap->UnlockBits(pBitmapData);
gdiBitmap->UnlockBits(&bitmapData);
// Draw it
_graphics->DrawImage(gdiBitmap, 0, 0, bitmap->Width, bitmap->Height);
}
Run Code Online (Sandbox Code Playgroud)
希望这对其他人有帮助 - 尚未在网络上找到任何实际将托管 GDI+ 位图转换为非托管 GDI+ 位图的代码片段。
谢谢大家的意见。
干杯,P