Changements dans le modèle de programmation
Les sections suivantes décrivent plusieurs différences entre la programmation avec Windows GDI+ et la programmation avec l’interface Windows Graphics Device Interface (GDI).
- Contexte de l’appareil, handles et objets graphiques
- Deux façons de tracer une ligne
- Stylos, pinceaux, chemins, images et polices en tant que paramètres
- Surcharge des méthodes
- Fin de la position actuelle
- Méthodes distinctes pour tracer et remplir
- Construction de régions
Contexte de l’appareil, handles et objets graphiques
Si vous avez déjà programmé en utilisant GDI (l’interface de périphérique graphique incluse dans les versions précédentes de Windows), vous connaissez l’idée d’un contexte de périphérique (DC). Un contexte de périphérique est une structure utilisée par Windows pour stocker des informations sur les capacités d’un périphérique d’affichage particulier et les attributs qui spécifient comment les éléments seront dessinés sur ce périphérique. Un contexte de périphérique pour un affichage vidéo est également associé à une fenêtre particulière sur l’affichage. Vous devez d’abord obtenir un handle pour un contexte de périphérique (HDC), puis passer ce handle en tant qu’argument aux fonctions GDI qui effectuent effectivement le dessin. Vous passez également le handle en tant qu’argument aux fonctions GDI qui obtiennent ou définissent les attributs du contexte de périphérique.
Lorsque vous utilisez GDI+, vous n’avez pas à vous préoccuper autant des handles et des contextes de périphériques que lorsque vous utilisez GDI. Il vous suffit de créer un objet Graphics puis d’invoquer ses méthodes dans le style orienté objet familier : myGraphicsObject.DrawLine(paramètres). L’objet Graphics est au cœur de GDI+, tout comme le contexte de périphérique est au cœur de GDI. Le contexte de périphérique et l’objet Graphics jouent des rôles similaires, mais il existe des différences fondamentales entre le modèle de programmation basé sur les handles utilisé avec les contextes de périphériques (GDI) et le modèle orienté objet utilisé avec les objets Graphics (GDI+).
L’objet Graphics, comme le contexte de périphérique, est associé à une fenêtre particulière à l’écran et contient des attributs (par exemple, le mode de lissage et l’indication de rendu du texte) qui spécifient comment les éléments doivent être dessinés. Cependant, l’objet Graphics n’est pas lié à un stylo, pinceau, chemin, image ou police, comme c’est le cas pour un contexte de périphérique. Par exemple, avec GDI, avant de pouvoir utiliser un contexte de périphérique pour tracer une ligne, vous devez appeler SelectObject pour associer un objet stylo au contexte de périphérique. Cela est appelé sélectionner le stylo dans le contexte de périphérique. Toutes les lignes tracées dans le contexte de périphérique utiliseront ce stylo jusqu’à ce que vous sélectionniez un autre stylo. Avec GDI+, vous passez un objet Pen en tant qu’argument à la méthode DrawLine de la classe Graphics. Vous pouvez utiliser un objet Pen différent dans chaque série d’appels DrawLine sans avoir à associer un objet Pen avec un objet Graphics.
Deux façons de tracer une ligne
Les deux exemples suivants tracent chacun une ligne rouge d’une épaisseur de 3, de la position (20, 10) à la position (200, 100). Le premier exemple appelle GDI, et le second appelle GDI+ via l’interface de classe C++.
Tracer une ligne avec GDI
Pour tracer une ligne avec GDI, vous avez besoin de deux objets : un contexte de périphérique et un stylo. Vous obtenez un handle pour un contexte de périphérique en appelant BeginPaint et un handle pour un stylo en appelant CreatePen. Ensuite, vous appelez SelectObject pour sélectionner le stylo dans le contexte de périphérique. Vous définissez la position du stylo à (20, 10) en appelant MoveToEx et vous tracez ensuite une ligne à partir de cette position de stylo jusqu’à (200, 100) en appelant LineTo. Notez que MoveToEx et LineTo reçoivent tous deux hdc en tant qu’argument.
HDC hdc;
PAINTSTRUCT ps;
HPEN hPen;
HPEN hPenOld;
hdc = BeginPaint(hWnd, &ps);
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
hPenOld = (HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc, 20, 10, NULL);
LineTo(hdc, 200, 100);
SelectObject(hdc, hPenOld);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
Tracer une ligne avec GDI+ et l’interface de classe C++
Pour tracer une ligne avec GDI+ et l’interface de classe C++, vous avez besoin d’un objet Graphics et d’un objet Pen. Notez que vous ne demandez pas à Windows des handles pour ces objets. Au lieu de cela, vous utilisez des constructeurs pour créer une instance de la classe Graphics (un objet Graphics) et une instance de la classe Pen (un objet Pen). Tracer une ligne implique d’appeler la méthode Graphics::DrawLine de la classe Graphics. Le premier paramètre de la méthode Graphics::DrawLine est un pointeur vers votre objet Pen. C’est un schéma plus simple et plus flexible que de sélectionner un stylo dans un contexte de périphérique comme le montre l’exemple précédent avec GDI.
HDC hdc;
PAINTSTRUCT ps;
Pen* myPen;
Graphics* myGraphics;
hdc = BeginPaint(hWnd, &ps);
myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics = new Graphics(hdc);
myGraphics->DrawLine(myPen, 20, 10, 200, 100);
delete myGraphics;
delete myPen;
EndPaint(hWnd, &ps);
Stylos, pinceaux, chemins, images et polices en tant que paramètres
Les exemples précédents montrent que les objets Pen peuvent être créés et maintenus séparément de l’objet Graphics qui fournit les méthodes de dessin. Les objets Brush, GraphicsPath, Image, et Font peuvent également être créés et maintenus séparément de l’objet Graphics. De nombreuses méthodes de dessin fournies par la classe Graphics reçoivent un objet Brush, GraphicsPath, Image ou Font en tant qu’argument. Par exemple, l’adresse d’un objet Brush est passée en tant qu’argument à la méthode FillRectangle, et l’adresse d’un objet GraphicsPath est passée en tant qu’argument à la méthode Graphics::DrawPath. De même, les adresses des objets Image et Font sont passées aux méthodes DrawImage et DrawString. Cela contraste avec GDI où vous sélectionnez un pinceau, un chemin, une image ou une police dans le contexte de périphérique et passez ensuite un handle au contexte de périphérique en tant qu’argument d’une fonction de dessin.
Surcharge des méthodes
De nombreuses méthodes de GDI+ sont surchargées ; c’est-à-dire que plusieurs méthodes partagent le même nom mais ont des listes de paramètres différentes. Par exemple, la méthode DrawLine de la classe Graphics se présente sous les formes suivantes :
Status DrawLine(IN const Pen* pen,
IN REAL x1,
IN REAL y1,
IN REAL x2,
IN REAL y2);
Status DrawLine(IN const Pen* pen,
IN const PointF& pt1,
IN const PointF& pt2);
Status DrawLine(IN const Pen* pen,
IN INT x1,
IN INT y1,
IN INT x2,
IN INT y2);
Status DrawLine(IN const Pen* pen,
IN const Point& pt1,
IN const Point& pt2);
Toutes les quatre variations de DrawLine reçoivent un pointeur vers un objet Pen, les coordonnées du point de départ et les coordonnées du point d’arrivée. Les deux premières variations reçoivent les coordonnées sous forme de nombres flottants, et les deux dernières reçoivent les coordonnées sous forme d’entiers. Les première et troisième variations reçoivent les coordonnées sous forme d’une liste de quatre nombres distincts, tandis que les deuxième et quatrième variations reçoivent les coordonnées sous forme de paire d’objets Point (ou PointF).
Fin de la position actuelle
Notez que dans les méthodes DrawLine présentées précédemment, à la fois le point de départ et le point d’arrivée de la ligne sont reçus en tant qu’arguments. Cela diffère du schéma GDI où vous appelez MoveToEx pour définir la position actuelle du stylo, suivie de LineTo pour tracer une ligne commençant à (x1, y1) et se terminant à (x2, y2). GDI+ dans son ensemble a abandonné la notion de position actuelle.
Méthodes distinctes pour tracer et remplir
GDI+ est plus flexible que GDI en ce qui concerne le traçage des contours et le remplissage des intérieurs de formes comme des rectangles. GDI dispose d’une fonction Rectangle qui trace le contour et remplit l’intérieur d’un rectangle en une seule étape. Le contour est tracé avec le stylo actuellement sélectionné, et l’intérieur est rempli avec le pinceau actuellement sélectionné.
hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Rectangle(hdc, 100, 50, 200, 80);
GDI+ dispose de méthodes distinctes pour tracer le contour et remplir l’intérieur d’un rectangle. La méthode DrawRectangle de la classe Graphics a l’adresse d’un objet Pen en tant que l’un de ses paramètres, et la méthode FillRectangle a l’adresse d’un objet Brush en tant que l’un de ses paramètres.
HatchBrush* myHatchBrush = new HatchBrush(
HatchStyleCross,
Color(255, 0, 255, 0),
Color(255, 0, 0, 255));
Pen* myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics.FillRectangle(myHatchBrush, 100, 50, 100, 30);
myGraphics.DrawRectangle(myPen, 100, 50, 100, 30);
Notez que les méthodes FillRectangle et DrawRectangle de GDI+ reçoivent des arguments qui spécifient le bord gauche, le haut, la largeur et la hauteur du rectangle. Cela contraste avec la fonction Rectangle de GDI, qui prend des arguments spécifiant le bord gauche, le bord droit, le haut et le bas du rectangle. Notez également que le constructeur de la classe Color dans GDI+ a quatre paramètres. Les trois derniers paramètres sont les valeurs habituelles pour le rouge, le vert et le bleu ; le premier paramètre est la valeur alpha, qui spécifie dans quelle mesure la couleur dessinée est mélangée avec la couleur d’arrière-plan.
Construction de régions
GDI fournit plusieurs fonctions pour créer des régions : CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn et CreatePolyPolygonRgn. On pourrait s’attendre à ce que la classe Region dans GDI+ ait des constructeurs analogues qui prennent des rectangles, des ellipses, des rectangles arrondis et des polygones en tant qu’arguments, mais ce n’est pas le cas. La classe Region dans GDI+ fournit un constructeur qui reçoit une référence à un objet Rect et un autre constructeur qui reçoit l’adresse d’un objet GraphicsPath. Si vous voulez construire une région basée sur une ellipse, un rectangle arrondi ou un polygone, vous pouvez facilement le faire en créant un objet GraphicsPath (qui contient une ellipse, par exemple) puis en passant l’adresse de cet objet GraphicsPath à un constructeur Region.
GDI+ permet de former facilement des régions complexes en combinant des formes et des chemins. La classe Region a des méthodes Union et Intersect que vous pouvez utiliser pour augmenter une région existante avec un chemin ou une autre région. Un des avantages du schéma GDI+ est qu’un objet GraphicsPath n’est pas détruit lorsqu’il est passé en tant qu’argument à un constructeur Region. Dans GDI, vous pouvez convertir un chemin en région avec la fonction PathToRegion, mais le chemin est détruit dans le processus. De plus, un objet GraphicsPath n’est pas détruit lorsque son adresse est passée en tant qu’argument à une méthode Union ou Intersect, ce qui vous permet d’utiliser un chemin donné comme élément de base pour plusieurs régions distinctes. Cela est illustré par l'exemple suivant. Supposons que onePath soit un pointeur vers un objet GraphicsPath (simple ou complexe) qui a déjà été initialisé.
Region region1(rect1);
Region region2(rect2);
region1.Union(onePath);
region2.Intersect(onePath);