GDI - Texte transparent sur Form transparent

GDI - Texte transparent sur Form transparent - C#/.NET managed - Programmation

Marsh Posté le 24-08-2010 à 15:35:39    

Bonjour,
 
Je suis sur un projet test qui m'affiche un Windows Form qui suit la souris. Le Form est constitué d'un PNG (type splashscreen) dont j'obtiens la transparence en retapant les fonctions natives Win32 GDI. Le truc c'est que j'aimerais afficher un texte transparent qui suit ce Form à l'aide de TextOut. J'ai essayé de spécifier en paramètre le HDC du Form courant mais cela n'affiche rien... J'arrive à afficher du texte sur le DC du screen cependant, mais il ne suit pas mon form du coup...
 
Pour ce qui est du code, tout se passe dans la méthode Form_Paint(object sender, PaintEventArgs e). Le HDC ici est nul, ce qui permet d'écrire sur le screen directement.
 

Code :
  1. IntPtr Hdc = API.GetDC(IntPtr.Zero);
  2. IntPtr memDc = API.CreateCompatibleDC(Hdc);
  3. IntPtr hBitmap = IntPtr.Zero;
  4. IntPtr oldBitmap = IntPtr.Zero;
  5. try
  6. {
  7.     //Display-image
  8.     Bitmap bmp = new Bitmap(backgroundImage);
  9.     hBitmap = bmp.GetHbitmap(Color.FromArgb(0));  //Set the fact that background is transparent
  10.     oldBitmap = API.SelectObject(memDc, hBitmap);
  11.    
  12.     //Display-rectangle
  13.     Size size = bmp.Size;
  14.     Point pointSource = new Point(0, 0);
  15.     Point topPos = new Point(this.Left, this.Top);
  16.     //Set up blending options
  17.     API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
  18.     blend.BlendOp = API.AC_SRC_OVER;
  19.     blend.BlendFlags = 0;
  20.     blend.SourceConstantAlpha = alpha;
  21.     blend.AlphaFormat = API.AC_SRC_ALPHA;
  22.     API.SetBkMode(Hdc, API.MODE_TRANSPARENT);
  23.     API.TextOut(Hdc, this.Left, this.Top, text, text.Length);
  24.     API.UpdateLayeredWindow(this.Handle, Hdc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, API.ULW_ALPHA);
  25.     //Clean-up
  26.     bmp.Dispose();
  27.     API.ReleaseDC(IntPtr.Zero, Hdc);
  28.    
  29.     if (hBitmap != IntPtr.Zero)
  30.     {
  31.         API.SelectObject(memDc, oldBitmap);
  32.         API.DeleteObject(hBitmap);
  33.     }
  34.     API.DeleteDC(memDc);
  35. }
  36. catch (Exception)
  37. {
  38. }


 
Pour la classe API qui permet d'utiliser les fonctions natives :
 

Code :
  1. internal class API
  2.     {
  3.         public const byte AC_SRC_OVER = 0x00;
  4.         public const byte AC_SRC_ALPHA = 0x01;
  5.         public const Int32 ULW_ALPHA = 0x00000002;
  6.         [StructLayout(LayoutKind.Sequential, Pack = 1)]
  7.         public struct BLENDFUNCTION
  8.         {
  9.             public byte BlendOp;
  10.             public byte BlendFlags;
  11.             public byte SourceConstantAlpha;
  12.             public byte AlphaFormat;
  13.         }
  14.        
  15.         [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
  16.         public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
  17.         [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
  18.         public static extern IntPtr GetDC(IntPtr hWnd);
  19.         [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  20.         public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
  21.         [DllImport("user32.dll", ExactSpelling = true)]
  22.         public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
  23.         [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  24.         public static extern bool DeleteDC(IntPtr hdc);
  25.         [DllImport("gdi32.dll", ExactSpelling = true)]
  26.         public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
  27.         [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  28.         public static extern bool DeleteObject(IntPtr hObject);
  29.         [DllImport("gdi32.dll", CharSet = CharSet.Auto)]
  30.         public static extern int TextOut(IntPtr hDC, int x, int y, string lpString, int nCount);
  31.         [DllImport("gdi32.dll" )]
  32.         public static extern int SetBkMode(IntPtr hdc, int mode);
  33.         // Background Modes
  34.         public const int MODE_TRANSPARENT = 1;
  35.         public const int MODE_OPAQUE = 2;
  36.     }


 
J'utilise cette méthode là car la transparence marche plutôt bien, j'aimerais juste pouvoir rajouter du texte à côté en plus. Si quelqu'un voit où est le pépin...
 
Merci d'avance!

Reply

Marsh Posté le 24-08-2010 à 15:35:39   

Reply

Marsh Posté le 24-08-2010 à 20:38:13    

Mouais, c'est plus compliqué que ça. Le problème c'est que l'API TextOut ne sait pas gérer les bitmaps avec canal alpha. J'imagine que tu dessines ton texte dans une partie de l'image ou le canal alpha vaut 0 ou presque (ie: complètement transparent).
 
Ton texte est bien là, mais avec un canal alpha valant 0... donc invisible. Si tu utilisais le mode opaque pour dessiner le texte, tu verrais l'arrière plan entourant le texte transparent. Si tu tiens vraiment à utiliser GDI, va falloir sortir l'artillerie lourde : dessiner ton texte dans un bitmap offscreen, puis utiliser la fonction AlphaBlend pour combiner le tout (en calculant le canal alpha à la main et en faisant gaffe d'utiliser des bitmap pré-multipliés). Yäy, c'est le fun GDI!¡!
 
Sinon, en .net, il me semble que tu ais directement accès à GDI+, tu devrais regarder de ce coté, car cette lib supporte l'écriture de texte avec canal alpha, avec une API un peu moins pourrie que GDI.

Reply

Marsh Posté le 25-08-2010 à 12:34:43    

Merci beaucoup pour ta réponse je vais voir ce que je peux faire!
 
Pour ce qui est de mon utilisation de GDI c'est juste que j'ai trouvé un projet sur le net qui utilise par interop les fonctions de gdi32.dll (splashscreen png transparent sans screenshot ou quoi). Je l'ai adapté à ma sauce du coup j'essaie d'afficher du texte en appelant pareil les fonctions de gdi.
 
Après si on peut faire ça simplement en gdi+ je suis preneur...

Reply

Marsh Posté le 26-08-2010 à 16:04:08    

Je regarde depuis hier ce qu'il se dit sur le sujet sur le net et c'est pas très positif concernant GDI+ et la transparence... La plupart des projets qui permettent une transparence d'une winform avec un png dessus utilisent les fonctions natives GDI et c'est une sacré prise de tête...
 
J'ai du mal à croire que rien n'a été fait en C# pour permettre une bonne gestion de la transparence des PNG sur des form sans bordures (sans passer par des appels aux fonctions de gdi32.dll par ex)... Quelqu'un a des infos là dessus ?
 
Merci !

Reply

Marsh Posté le 26-08-2010 à 17:47:50    

Chuis pas sûr de ce que tu veux faire exactement. De ce que j'avais compris tu as un splash screen semi-transparent, tu voudrais coller du texte, genre version, build, etc ... sur une partie transparente. Normallement ça devrait fonctionner avec GDI+, par exemple un test vite fait à l'arrache :
 
http://www.izipik.com/images/201008/26/lgsmo4-layered-window-text-gdi_-alpha-channel.png
 
La fenêtre en question est celle contenant la tasse de café. Il y a une fenêtre explorer juste en dessous. Le texte "Hello world" à été rajouté avec GDI+, avec un code du style à appeler juste avant UpdateLayeredWindow() :
 

Code :
  1. HDC        hdc = (HDC)GFX_GetDevice((GC)gc);
  2. Graphics   graphics(hdc);
  3. FontFamily fontFamily(L"Arial" );
  4. Font       font(&fontFamily, 12, FontStyleRegular, UnitPixel);
  5. PointF     point(10, 10);
  6. SolidBrush brush(Color(0,0,0));
  7. WCHAR      str[] = L"Hello world !";
  8.  
  9. graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
  10. for (int i = 10; i < 100; i += 10)
  11. {
  12.     point.X = point.Y = i;
  13.     brush.SetColor(Color(255, i * 255 / 100, 0));
  14.     graphics.DrawString(str, -1, &font, point, &brush);
  15. }


 
Avec GDI, ton texte serait invisible sauf dans les parties ou le canal alpha > 0. C'est ce que tu voulais faire ?
 
Edit: c'est en C++, pas en .NET, mais l'API est très similaire.


Message édité par tpierron le 26-08-2010 à 17:48:38
Reply

Marsh Posté le 27-08-2010 à 12:24:21    

Merci beaucoup pour ta réponse! En effet ça a l'air de bien marcher comme ça. J'ai récemment décidé de passer au WPF finalement et ça marche niquel. Je voulais utiliser le GDI/GDI+ pour rester en .NET 2.0 car je développe par dessus une appli 2.0 mais l'interop fonctionne à merveille donc aucun soucis !
 
Merci beaucoup!

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed