2012-05-24 11 views
6

Gráficos Pasar objeto entre C++ nativo y C#La pérdida de anti-aliasing cuando se comparten entre el objeto Graphics código administrado y no administrado

Actualmente estoy trabajando en una aplicación .NET-como el Paint. Tengo varios tipos de capas que se implementan en C#. Estas capas se dibujan en un objeto de gráficos .NET proporcionado por un control de usuario de WinForms; es similar al control canvas de WPF. La clase base de capa tiene un método Draw que se implementa como sigue:

public void Draw(IntPtr hdc) 
{ 
    using (var graphics = Graphics.FromInternalHDC(hdc) 
    { 
     // First: Setup rendering settings like SmoothingMode, TextRenderingHint, ... 
     // Layer specific drawing code goes here... 
    } 
} 

Para problemas de rendimiento y la descompilación de que estoy haciendo la composición de las capas en un conjunto de modo mixto ya que también estoy aplicando efectos como bisel o sombra paralela El contenedor, por supuesto, escrito en C++/CLI, se llama directamente desde el control de lienzo y reenvía los metadatos de cada capa y el objeto de gráficos de destino (objeto de gráficos desde mi control de usuario de lienzo escrito C#) a una clase nativa de C++.

C++/CLI Envoltura:

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 
     }; 
} 

nativo C++ Procesador:

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);   
    } 
    } 
} 

Hasta aquí todo bien. El código anterior funciona casi como se esperaba, pero ...

Problema

La única cosa es que mi implementación actual falta de anti-aliasing y semi transparencia cuando se representa PNG con sombras por ejemplo. Así que solo tengo 2 valores para el canal Alpha: Transparente o Color visible completo en 255. Este efecto secundario hace que dibujar PNG con canal alfa y las fuentes se vean muy feas. No puedo obtener el mismo anti aliasing semitransparente suave y agradable más como antes cuando trabajé con código puro C#.

PERO: Al dibujar una cadena en los gráficos nativos objeto directamente,

layerGraphics->DrawString(...); 

anti-aliasing y semi transparencia están de vuelta para siempre. Entonces, el problema solo es evidente al pasar Graphics HDC a .NET.

Preguntas

¿Hay alguna solución/solución para este problema? Intenté crear el mapa de bits directamente en la clase Capa C# y devolver el IntPtr para el HBITMAP al código nativo. Este enfoque funciona, pero en este caso tengo otro problema ya que no puedo encontrar una solución perfecta para convertir HBITMAP en GDI + mapa de bits con canal alfa (el ruido blanco de píxeles rodea los bordes al dibujar fuentes).

¡Gracias por su contribución! :)

Solución de demostración

Adjunto encontrará una solución de demostración aquí: Sources

En esta solución demostración Estoy probando 3 métodos de representación diferentes (implementan en NativeRenderer.CPP), mientras que el primero muestra los problemas descritos:

Demo output

1) RenderViaBitmapFromCSharp()-a) Crea un nuevo mapa de bits en C++, crea un nuevo objeto Graphics en C++, llama a la C# código de dibujo pasando los Gráficos objeto de C++ HDC - falla

Pero:b) dibujo directamente desde C++ funciona a través del mapa de bits creado demasiado

2) RenderDirectlyFromCSharp() - Crea un nuevo objeto Graphics desde C# Gráficos de manejar en C++ , llama al código de dibujo C# pasando el C++ objeto Graphics HDC - Works

3) RenderDirectlyFromCPP () - Crea un nuevo objeto Graphics de C# Gráficos de manejar en C++, dibuja el texto directamente en C++ - Obras

+0

C++/CLI ve un poco ... código C#! De todos modos, no veo el código que utilizas para escribir la cadena en C++, así que supongo un poco: si estás usando funciones GDI nativas (para metaData.layerDrawDelegate (lgHDC)), la cadena no se representará con la configuración que tenido con GDI + (= transparencia, antialias y cualquier otra cosa de GDI + no se aplicará a una llamada de GDI). –

+0

He cambiado el pseudo código para que sea más C++/CLI. El método para escribir la cadena se acaba de copiar y pegar recortado de la explicación de MSDN de DrawString, nada especial. En el método Dibujar de la capa estoy configurando todas esas configuraciones como SmoothingMode o TextRenderHint al mismo que en el objeto nativo GDI +. – barnacleboy

+0

Las funciones de GDI, como 'ExtTextOut' no admiten un canal alfa, y simplemente pasar un DC con un mapa de bits seleccionado con un canal alfa no los hará alfabéticos. –

Respuesta

1
Graphics graphics(targetGraphicsHDC); 

Está creando un nuevo objeto de gráficos. Por lo tanto, no tendrá sus propiedades configuradas como lo hizo su original. Propiedades como TextRenderingHint no son propiedades de un contexto de dispositivo GDI, son específicas de Gráficos.

Problema bastante feo, tendrá que volver a inicializar el objeto Graphics de la forma en que se hizo en el código de llamada. Eso es dos pedazos de código que están muy separados el uno del otro. Evitar la conversión a HDC y viceversa es la única manera realmente decente de evitar el problema.

+0

¿Cómo puedo evitar la conversión a HDC, ya que no puedo trabajar de forma nativa con el objeto de gráficos .NET inicial? ¿Podría por favor dar un ejemplo de cómo reinicializar los gráficos de Gdiplus en el modo .NET? ¡Gracias! – barnacleboy

+0

Además: 'Graphics graphics (targetGraphicsHDC)' de hecho no es un problema ya que mi código fuente muestra, el problema es la línea 'Graphics layerGraphics (layerBitmap)' ... – barnacleboy

+0

No hay una solución simple. Rick Brewster de la fama de Paint.NET escribe mucho código C++/CLI. Eso es tener tu pastel y comértelo también, puede usar el objeto de gráficos administrado * y * hacer llamadas a funciones nativas cuando sea necesario para la velocidad. –

1

Terminé creando el mapa de bits en C# y pasando el objeto a C++/CLI. Como ya mencionaron Hans y Vincent, debes evitar GetHDC. Así que mi solución lee en pseudo código de la siguiente manera:

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; 
} 

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); 
} 

la esperanza de que es de utilidad para otros - tiene no se ha encontrado ningún fragmento de código en la web que realmente convierta GDI + Bitmap gestionado a no gestionado.

Gracias a todos por sus comentarios.

Saludos, P

código
Cuestiones relacionadas