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