API Windows en C

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

CGI

♪

- Titre : API Windows en C

- Auteur : CGI

- Parution : 24 avril 2004

- Licence : 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.

Sommaire

- I. Introduction

- II. Explication

- III. Code de l'exemple

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.

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

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

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

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

#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