Developpez.com

Plus de 2 000 forums
et jusqu'à 5 000 nouveaux messages par jour

Developpez.com - C
X

Choisissez d'abord la catégorieensuite la rubrique :



API Windows en C

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

Par CGi

Le 24 avril 2004


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.

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 plus part 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. .



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.)

       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.

       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.)

       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 BeginPath et 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.

       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.

       BeginPath(hdc);
           TextOut(hdc, 10, 10, "Bonjour", 7);
       EndPath(hdc);
       SelectClipPath(hdc, RGN_XOR);

La découpe ce trouve maintenant en sorte qu'elle ne fait plus partie du contexte de périphérique. Donc tout dessins 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émonter ceci nous allons dessiner des traits horizontaux sur la surface de dessin dans une boucle.

       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.



Code de l'exemple :

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



Bon découpage. CGi

Avec la contribution de Demco pour la relecture.



Sommaire



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.

Assembleur
  Assembleur sous Visual C++.

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.

Java
  Applet java.





Copyright 2002-2016 CGi - Tous droits réservés CGi. Toutes reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation écrite de la part de l'auteur, propriétaire des droits intellectuels.
Les codes sources de ce document sont fournis en l'état. L'utilisateur les utilise à ses risques et périls, sans garantie d'aucune sorte de la part de l'auteur. L'auteur n'est responsable d'aucun dommage subi par l'utilisateur pouvant résulter de l'utilisation ou de la distribution des codes sources de ce document.
De la même façon, l'auteur n'est en aucun cas responsable d'une quelconque perte de revenus ou de profits, ou de données, ou de tous dommages directs ou indirects, susceptibles de survenir du fait de l'utilisation des codes sources de ce document, quand bien même l'auteur aurait été averti de la possibilité de tels dommages. L'utilisation des codes sources de ce document vaut acceptation par l'utilisateur des termes de la licence ci-dessus.

Contacter le responsable de la rubrique C