Передача объекта Graphics между собственным C++ и C#
В настоящее время я работаю над приложением, подобным Paint .NET. У меня есть несколько типов слоев, которые реализованы на С#. Эти слои рисуются в объекте .NET Graphics, который предоставляется пользовательским элементом управления WinForms — он похож на элемент управления холста 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...
}
}
Из соображений производительности и декомпиляции я выполняю композицию слоев в сборке смешанного режима, поскольку я также применяю такие эффекты, как скос или тень. Оболочка, написанная, конечно же, на C++/CLI, вызывается напрямую из элемента управления холстом и перенаправляет метаданные каждого слоя и целевого объекта Graphics (объект Graphics из моего написанного на C# пользовательского элемента управления холстом) в собственный класс C++.
C++/CLI Wrapper:
public ref class RendererWrapper
{
public:
void Render(IEnumerable^ 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
};
}
Native C++ Renderer:
class NativeRenderer
{
void NativeRenderer::Render(vector 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 значения для альфа-канала: прозрачный или полностью видимый цвет при 255. Этот побочный эффект делает рисование PNG с альфа-каналом и шрифтами очень уродливым.Я больше не могу получить такое же гладкое и приятное полупрозрачное сглаживание, как раньше, когда я работал с чистым кодом C#.
НО: при рисовании строки непосредственно в родном объекте Graphics,
layerGraphics->DrawString(...);
сглаживание и полупрозрачность возвращаются навсегда. Таким образом, проблема проявляется только при передаче Graphics HDC в .NET.
Вопросы
Есть ли решение/обходной путь для этой проблемы? Я попытался создать растровое изображение непосредственно в классе слоя С# и вернуть IntPtr для HBITMAP в собственный код. Этот подход работает, но в этом случае у меня есть другая проблема, так как я не могу найти идеальное решение для преобразования HBITMAP в GDI+ Bitmap с альфа-каналом (белый пиксельный шум окружает края при рисовании шрифтов).
Спасибо за ваш вклад! :)
Демо-решение
В приложении вы найдете демо-решение здесь: Исходники
В этом демонстрационном решении я тестирую 3 различных метода рендеринга (все они реализованы в NativeRenderer.cpp), в то время как ПЕРВЫЙ показывает описанные проблемы:
1) RenderViaBitmapFromCSharp()- a)Создает новое растровое изображение в C++, создает новый объект Graphics в C++, вызывает код рисования C#, передавая объект C++ Graphics HDC - Сбой
Но:b)Рисование напрямую из C++ также работает через созданное растровое изображение
2) RenderDirectlyFromCSharp( )— создает новый объект Graphics из графического дескриптора C# в C++. , вызывает код рисования C#, передавая объект Graphics C++ HDC - Работает
3) RenderDirectlyFromCPP()- Создает новый объект Graphics из дескриптора C# Graphics в C++, рисует текст непосредственно в C++ - Работает