Потеря сглаживания при совместном использовании объекта Graphics между управляемым и неуправляемым кодом

Передача объекта 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), в то время как ПЕРВЫЙ показывает описанные проблемы:

Demo output

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++ - Работает

6
задан barnacleboy 24 May 2012 в 11:34
поделиться