Windows GDI : Tracés et découpes de contours

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Dans cet article nous allons voir à l'aide d'un exemple comment tracer le contour d'un texte (contour des lettres), faire une découpe de ce contour et dessiner sur la découpe.

Image non disponible

Pour arriver à nos fins, nous utiliserons les fonctions de Paths et de Clipping du GDI de Windows. Le langage choisi sera le C et nous utiliserons l'API Windows pour que notre exemple soit compatible avec la plupart des compilateurs C et C++. Personnellement je l'ai compilé sur BCB4, BCB6 et Dev C++.
Dans cet article nous ne reviendrons pas sur l'architecture des applications Windows, pour cela vous pouvez aller consulter ce tutoriel : API Windows : La fenêtre principale. .

II. Explication

Nous créons donc une application traditionnelle Windows avec sa fonction d'entrée WinMain, nous y créons une fenêtre et sa procédure de fenêtre nommée pour l'occasion WndProc. Voir code complet en bas de ce document.

Nous allons nous concentrer sur le code exécuté à la réception du message WM_PAINT (je l'ai mis dans un if au lieu de l'habituel switch case juste pour des raisons de clarté du code).

Tout d'abord nous récupérons un contexte de périphérique HDC à l'aide de la fonction BeginPaint. Le dessin sur ce contexte sera réellement effectif lors de l'appel de la fonction EndPaint. (BeginPaint et EndPaint ne peuvent s'utiliser uniquement que dans le traitement d'un message WM_PAINT mais sont plus performantes que de dessiner sur un contexte de périphérique obtenu avec GetDC.)

 
Sélectionnez
       PAINTSTRUCT ps;

       HDC hdc;



       hdc = BeginPaint(hwnd, &ps);

           //Fonction de dessin ici

       EndPaint(hwnd, &ps);

Ensuite nous créons une fonte à l'aide de la fonction CreateFontIndirect qui utilise une structure LOGFONT initialisée avec les caractéristiques de la fonte dont nous mettrons la taille suffisamment importante pour la visibilité dans l'exemple. Puis nous sélectionnons cette fonte pour le contexte de périphérique précédemment obtenu.

 
Sélectionnez
       LOGFONT lf;

       HFONT hfont;

       HFONT hfontOld;

       char f[32]={"Arial Black"};



       ZeroMemory(&lf, sizeof(LOGFONT));

       strcpy(lf.lfFaceName,f);

       lf.lfHeight = 100;



       hfont = CreateFontIndirect(&lf);

       hfontOld = (HFONT)SelectObject(hdc, hfont);



       //Utilisation ici



       SelectObject(hdc, hfontOld);

       DeleteObject(hfont);

Même opération pour un crayon dont nous initialiserons la taille à 3 pixels. (Ne pas oublier de détruire les objets de dessin quand ils ne sont plus nécessaires.)

 
Sélectionnez
       HPEN hpen;

       HPEN hpenOld;



       hpen = CreatePen(PS_SOLID, 3, 0);

       hpenOld = (HPEN)SelectObject(hdc, hpen);



       //Utilisation ici



       SelectObject(hdc, hpenOld);

       DeleteObject(hpen);

Dessin du chemin :
Un chemin se prépare avant d'être dessiné, entre les fonctions BeginPathet EndPath qui reçoivent en paramètre un contexte de périphérique. (Entre ces deux fonctions, on peut utiliser toutes les fonctions de dessin du GDI.) Dans cet exemple nous dessinons donc un texte avec la fonction TextOut. C'est la fonction StrokePath qui dessine réellement le tracé préparé entre BeginPath et EndPath.

 
Sélectionnez
       BeginPath(hdc);

           TextOut(hdc, 10, 10, "Bonjour", 7);

       EndPath(hdc);

       StrokePath(hdc);

Nous aurions pu aussi utiliser la fonction FillPath qui peut remplir l'intérieur du chemin avec la brosse courante ou StrokeAndFillPath qui elle fait les deux. Quand vous avez appelé l'une de ces fonctions, le chemin n'est plus utilisable. Un tracé qui n'est pas fermé peut être fermé avec la fonction CloseFigure.

Remplissage d'une découpe :
Pour faire une découpe, il faut d'abord tracer son contour comme vu précédemment. Mais au lieu de le dessiner, nous le découperons avec la fonction SelectClipPath.

 
Sélectionnez
       BeginPath(hdc);

           TextOut(hdc, 10, 10, "Bonjour", 7);

       EndPath(hdc);

       SelectClipPath(hdc, RGN_XOR);

La découpe se trouve maintenant en sorte qu'elle ne fait plus partie du contexte de périphérique. Donc tout dessin fait sur ce contexte de périphérique ne sera pas tracé sur la découpe. La fonction SelectClipPath a plusieurs possibilités pour la découpe selon la valeur de son deuxième argument : RGN_AND, RGN_COPY, RGN_DIFF, RGN_OR, RGN_XOR.
Pour vous démontrer ceci, nous allons dessiner des traits horizontaux sur la surface de dessin dans une boucle.

 
Sélectionnez
       SIZE sz;

       int i;



       GetTextExtentPoint32(hdc, "Bonjour",7 , &sz);

       for (i = 121; i < (120 + sz.cy); i += 6) {

          MoveToEx(hdc, 11, i, (LPPOINT) NULL);

          LineTo(hdc, (9 + sz.cx), i);

       }

La fonction GetTextExtentPoint32 sert ici à récupérer les dimensions du rectangle qui contient le texte préalablement tracé. Les traits seront donc dessinés seulement dans ce rectangle ceci juste dans un but d'esthétique.

III. Code de l'exemple

 
Sélectionnez
#include <windows.h>



HWND hwndMain ;



LONG APIENTRY WndProc(HWND hwnd, UINT uMsg, UINT wParam, LONG lParam);



int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

                                        LPSTR lpCmdLine, int nCmdShow)

{

        MSG msg;

        WNDCLASS wc;



        // Enregistrement de la classe de fenêtre.

        wc.style = 0;

        wc.lpfnWndProc = (WNDPROC) WndProc;

        wc.cbClsExtra = 0;

        wc.cbWndExtra = 0;

        wc.hInstance = hInstance;

        wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);

        wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);

        wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

        wc.lpszMenuName =  NULL;

        wc.lpszClassName = "MainWndClass";

        if (!RegisterClass(&wc))  return FALSE;     



        // Création de la fenêtre. 

        hwndMain = CreateWindow("MainWndClass", "Path and Clip",

                 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

                            360, 280,  NULL,  NULL, hInstance,  NULL);

        ShowWindow(hwndMain, nCmdShow);

        UpdateWindow(hwndMain);

 

       // Et la fameuse boucle de messages

       while (GetMessage(&msg, NULL, 0, 0))

       {

          TranslateMessage(&msg);

          DispatchMessage(&msg);

       }

       return 0;

}



LONG APIENTRY WndProc(HWND hwnd, UINT uMsg, UINT wParam, LONG lParam)

{

 if (uMsg == WM_PAINT)

  {

       LOGFONT lf;

       PAINTSTRUCT ps;

       HFONT hfont;

       HFONT hfontOld;

       HPEN hpen;

       HPEN hpenOld;

       SIZE sz;

       int i;

       HDC hdc;

       char f[32]={"Arial Black"};





       hdc = BeginPaint(hwnd, &ps);



       // Initialisation de la structure LOGFONT 

       ZeroMemory(&lf, sizeof(LOGFONT));

       strcpy(lf.lfFaceName,f);

       lf.lfHeight = 100;

   

       // Céation de la fonte 

       hfont = CreateFontIndirect(&lf);

       hfontOld = (HFONT)SelectObject(hdc, hfont);

   

       // Création du crayon 

       hpen = CreatePen(PS_SOLID, 3, 0);

       hpenOld = (HPEN)SelectObject(hdc, hpen);



       // Dessin du chemin 

       BeginPath(hdc);

           TextOut(hdc, 10, 10, "Bonjour", 7);

       EndPath(hdc);

       StrokePath(hdc);

   

       // Dessin dans la découpe du chemin 

       BeginPath(hdc);

           TextOut(hdc, 10, 120, "Bonjour", 7);

       EndPath(hdc);

       SelectClipPath(hdc, RGN_XOR);

   

       GetTextExtentPoint32(hdc, "Bonjour",7 , &sz);

       for (i = 121; i < (120 + sz.cy); i += 6) {

          MoveToEx(hdc, 11, i, (LPPOINT) NULL);

          LineTo(hdc, (9 + sz.cx), i);

       }



       SelectObject(hdc, hfontOld);

       SelectObject(hdc, hpenOld);

       DeleteObject(hpen);

       DeleteObject(hfont);



       EndPaint(hwnd, &ps);



       return 0;

  }



 if (uMsg == WM_DESTROY)

  {

       PostQuitMessage(0);

  }



 return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

Avec la contribution de Demco pour la relecture.

Bon découpage.

À vos PC.

CGi

Sommaire

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?
  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.
C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.
  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.
DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 CGI. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.