2012-05-24 22 views
6

Pas Grafik yerel C++ ve C#paylaşımı Grafik

Şu anda bir Boya .NET benzeri bir uygulama üzerinde çalışıyorum arasında nesne yönetilen ve yönetilmeyen kod arasındaki nesne zaman keskinliği yumuşatma ayarını kaybetmek. C# 'da uygulanan çok sayıda katman katmanım var. Bu katmanlar bir WinForms kullanıcı denetimi tarafından sağlanan bir .NET Graphics nesnesine çizilir - WPF kanvas kontrolüne benzer. aşağıdaki gibi tabaka temel sınıf uygulanan bir Çekme yöntemi vardır:

Ayrıca konik ya da bu gibi etkileri başvuruyorum yana karışık mod düzeneğinin katmanların bileşimin yaptığım performans ve Dekompilasyon sorunları için
public void Draw(IntPtr hdc) 
{ 
    using (var graphics = Graphics.FromInternalHDC(hdc) 
    { 
     // First: Setup rendering settings like SmoothingMode, TextRenderingHint, ... 
     // Layer specific drawing code goes here... 
    } 
} 

Düşen gölge. Tabii ki, C++/CLI ile yazılan sarıcı, doğrudan tuval kontrolünden çağrılır ve her katmanın meta verilerini ve hedef Graphics nesnesini (C# yazılı kanvas kullanıcı kontrolümün Grafik nesnesi) yerel C++ sınıfına iletir.

C++/CLI Sarıcı:

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

Yerli C++ Renderer: Şimdiye kadar iyi

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

. Yukarıdaki kod tek şey, örneğin gölgeler ile PNG işlerken benim şu anki uygulama anti-aliasing ve yarı şeffaflık eksik olmasıdır

Sorun

... beklendiği neredeyse çalışır, ancak. Bu yüzden sadece Alfa kanalı için sadece 2 değere sahibim: Şeffaf veya tam görünür 255 Renk. Bu yan etki, alfa kanalı ve yazı tiplerinin çok çirkin görünümlü PNG'lerini çiziyor. Saf C# kodu ile çalışırken daha önce olduğu gibi aynı pürüzsüz ve güzel yarı saydam anti-kenar yumuşatma alamıyorum.

ANCAK: yerli Graphics bir dize çizerken

layerGraphics->DrawString(...); 

karşıtı aliasing ve yarı şeffaflık geri iyi içindir, doğrudan nesne. Bu yüzden sorun sadece Grafik HDC’yi .NET’e geçirirken belirgindir.

Sorular

bu soruna bir çözüm/çözüm var mı? Bitmap'i doğrudan C# Layer sınıfında oluşturmaya çalıştım ve HBITMAP için IntPtr'i yerel koda döndürdüm. Bu yaklaşım işe yarıyor, ancak bu durumda HBITMAP'i alfa kanalı ile GDI + Bitmap'e dönüştürmek için mükemmel bir çözüm bulamadığım için başka bir problemim var (yazı çizerken beyaz piksel gürültü kenarları sarar).

Girişiniz için teşekkürler! :)

Demo Çözüm

burada bir demo çözüm bulacağız Ekli: Sources

3 farklı render yöntemleri (bütün NativeRenderer uygulanan test ediyorum bu demo çözümde.CPP), ilki açıklanan problemleri gösterirken:

Demo output

1) RenderViaBitmapFromCSharp()-a), C++, yeni bir bitmap oluşturur yeni grafik C++ nesne oluşturur, aramalar C++ Grafik geçirerek C# çizim kodu HDC nesne -

Ama başarısız: C++ doğrudan çizimb) çok yaratılan bitmap yoluyla çalışır

2) RenderDirectlyFromCSharp() - Works

3) RenderDirectlyFromCPP (- C++ , C++ Grafik HDC nesne geçirilerek C# çizim kodunu çağırmasını yeni grafik olarak ele C# Graphics nesne oluşturur) - yeni Grafik C++ C# Grafik gelen tanıtıcı nesne oluşturur C++ metni doğrudan çizer - İşleri

+0

C++/CLI kodu biraz ... C# kodu görünüyor P! Neyse, C++ 'da dize yazmak için kullandığınız kodu görmüyorum, bu yüzden biraz tahmin ediyorum: eğer yerel GDI fonksiyonlarını kullanıyorsanız (metaData.layerDrawDelegate (lgHDC) için), o zaman dizge ayarlarla işlenmez. GDI + ile (= şeffaflık, antialias ve GDI + 'dan başka herhangi bir şey GDI çağrısına uygulanmayacak) vardı. –

+0

Daha fazla C++/CLI olması için sözde kodu değiştirdim. Dize yazma yöntemi sadece özel bir şey değil, DrawString MSDN açıklamasından koparıp kopyalanır. Katmanın Çizme yönteminde, SmoothingMode veya TextRenderHint gibi tüm ayarları yerel GDI + nesnesindekiyle aynı olarak ayarlıyorum. – barnacleboy

+0

'ExtTextOut' gibi GDI işlevleri bir alfa kanalı desteklemez ve bir DC ile seçilen bir bitmap'i bir alfa kanalı ile geçirmek, bunları alfa'nın farkında olmalarını sağlamaz. –

cevap

1
Graphics graphics(targetGraphicsHDC); 

yeni Grafik nesnesini oluşturuyorsunuz. Bu yüzden özellikleri orijinaline göre ayarlanmayacak. TextRenderingHint gibi özellikler bir GDI aygıtı bağlamının özellikleri değildir, bunlar Grafiklere özgüdür.

Oldukça çirkin bir sorun, Graphics nesnesini arama kodunda yapıldığı gibi yeniden başlatmanız gerekecek. Bu birbirinden çok uzak iki kod parçası. HDC'ye dönüşümü ve geri dönüşü önlemek, sorunu düzeltmek için gerçekten iyi bir yoldur.

+0

İlk .NET Graphics nesnesiyle yerel olarak çalışamadığımdan, HDC'ye dönüşümü nasıl önleyebilirim? Gdiplus Grafiklerini .NET yolunda nasıl yeniden başlatabileceğinize dair bir örnek verebilir misiniz? Teşekkür ederim! – barnacleboy

+0

Ayrıca: "Grafik grafikleri (targetGraphicsHDC)' kaynak kodumun gösterdiği gibi bir problem değil, sorun "Graphics layerGraphics (layerBitmap)" satırıdır ... – barnacleboy

+0

Basit bir çözüm yoktur. Paint.NET ününün Rick Brewster'ı C++/CLI kodunun birçoğunu yazıyor. Bu sizin kekinizi yiyor ve yemek yiyor, yönetilen Grafik nesnesini kullanabilir * ve * hız için gerektiğinde yerel işlev çağrıları yapabilir. –

1

C# içinde Bitmap oluşturmayı ve nesneyi C++/CLI'ye iletmeyi bitirdim. Hans ve Vincent tarafından daha önce de belirtildiği gibi GetHDC'den kaçınmalısınız.

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++ şu şekildedir: Yani benim geçici çözüm sözde kod okur

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

Umut başkalarına yararlı olur - var Web üzerinde, aslında yönetilmeyen GDI + Bitmap'e dönüştürmeyi yapan herhangi bir kod parçacığı bulunamadı.

Yorumlarınız için hepinize teşekkür ederiz.

Alkış,

İlgili konular