Changements dans le modèle de programmation

Les sections suivantes décrivent plusieurs façons dont la programmation avec Windows GDI+ est différente de la programmation avec Windows Graphics Device Interface (GDI).

Contextes d’appareil, handles et objets graphiques

Si vous avez écrit des programmes à l’aide de GDI (l’interface de périphérique graphique incluse dans les versions précédentes de Windows), vous êtes familiarisé avec l’idée d’un contexte d’appareil (DC). Un contexte d’appareil est une structure utilisée par Windows pour stocker des informations sur les fonctionnalités d’un périphérique d’affichage particulier et des attributs qui spécifient comment les éléments seront dessinés sur cet appareil. Un contexte d’appareil pour un affichage vidéo est également associé à une fenêtre particulière sur l’écran. Tout d’abord, vous obtenez un handle pour un contexte d’appareil (HDC), puis vous passez ce handle en tant qu’argument aux fonctions GDI qui effectuent réellement le dessin. Vous passez également le handle en tant qu’argument aux fonctions GDI qui obtiennent ou définissent les attributs du contexte de l’appareil.

Lorsque vous utilisez GDI+, vous n’avez pas besoin d’être aussi concerné par les handles et les contextes d’appareil que lorsque vous utilisez GDI. Il vous suffit de créer un objet Graphics , puis d’appeler ses méthodes dans le style orienté objet familier : myGraphicsObject.DrawLine(parameters). L’objet Graphics se trouve au cœur de GDI+ tout comme le contexte de l’appareil est au cœur de GDI. Le contexte de l’appareil et l’objet Graphics jouent des rôles similaires, mais il existe des différences fondamentales entre le modèle de programmation basé sur le handle utilisé avec les contextes d’appareil (GDI) et le modèle orienté objet utilisé avec les objets Graphics (GDI+).

L’objet Graphics , comme le contexte de l’appareil, est associé à une fenêtre particulière à l’écran et contient des attributs (par exemple, le mode de lissage et l’indicateur de rendu du texte) qui spécifient la façon dont les éléments doivent être dessinés. Toutefois, l’objet Graphics n’est pas lié à un stylet, un pinceau, un chemin d’accès, une image ou une police en tant que contexte d’appareil. Par exemple, dans GDI, avant de pouvoir utiliser un contexte d’appareil pour tracer une ligne, vous devez appeler SelectObject pour associer un objet stylet au contexte de l’appareil. Il s’agit de la sélection du stylet dans le contexte de l’appareil. Toutes les lignes dessinées dans le contexte de l’appareil utilisent ce stylet jusqu’à ce que vous sélectionnons un autre stylet. 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 donné à un objet Graphics .

Deux façons de tracer un trait

Les deux exemples suivants dessinent chacun une ligne rouge de largeur 3 de l’emplacement (20, 10) à l’emplacement (200 100). Le premier exemple appelle GDI et le second appelle GDI+ via l’interface de classe C++.

Tracer un trait avec GDI

Pour tracer une ligne avec GDI, vous avez besoin de deux objets : un contexte d’appareil et un stylet. Vous obtenez un handle pour un contexte d’appareil en appelant BeginPaint, et un handle pour un stylet en appelant CreatePen. Ensuite, vous appelez SelectObject pour sélectionner le stylet dans le contexte de l’appareil. Vous définissez la position du stylet sur (20, 10) en appelant MoveToEx , puis vous dessinez une ligne à partir de cette position de stylet sur (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);

Dessin d’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 de handles pour ces objets. Au lieu de cela, vous utilisez des constructeurs pour créer un instance de la classe Graphics (un objet Graphics) et un instance de la classe Pen (objet Pen). Le dessin d’une ligne implique l’appel de la méthode Graphics::D rawLine de la classe Graphics . Le premier paramètre de la méthode Graphics::D rawLine est un pointeur vers votre objet Pen . Il s’agit d’un schéma plus simple et plus flexible que la sélection d’un stylet dans un contexte d’appareil, comme illustré dans l’exemple GDI précédent.

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

Stylets, 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 gérés 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 gérés séparément de l’objet Graphics. La plupart des 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::D rawPath . 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 l’appareil, puis passez un handle au contexte de l’appareil en tant qu’argument à une fonction de dessin.

Surcharge de méthode

La plupart des méthodes GDI+ sont surchargées ; autrement dit, 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);

Les quatre variantes DrawLine ci-dessus reçoivent un pointeur vers un objet Pen , les coordonnées du point de départ et les coordonnées du point de fin. Les deux premières variantes reçoivent les coordonnées sous forme de nombres à virgule flottante, tandis que les deux dernières reçoivent les coordonnées sous forme d’entiers. Les première et troisième variantes reçoivent les coordonnées sous la forme d’une liste de quatre nombres distincts, tandis que les deuxième et quatrième variantes reçoivent les coordonnées sous la forme d’une paire d’objets Point (ou PointF).

Plus de position actuelle

Notez que dans les méthodes DrawLine présentées précédemment, le point de départ et le point de fin de la ligne sont reçus en tant qu’arguments. Il s’agit d’un écart par rapport au schéma GDI où vous appelez MoveToEx pour définir la position actuelle du stylet suivi de LineTo afin de dessiner 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 Draw et Fill

GDI+ est plus flexible que GDI lorsqu’il s’agit de dessiner les contours et de remplir les intérieurs de formes comme des rectangles. GDI a une fonction Rectangle qui dessine le contour et remplit l’intérieur d’un rectangle en une seule étape. Le contour est dessiné avec le stylet 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+ a des méthodes distinctes pour dessiner 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 comme l’un de ses paramètres, et la méthode FillRectangle a l’adresse d’un objet Brush comme 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 dans GDI+ reçoivent des arguments qui spécifient le bord gauche, le haut, la largeur et la hauteur du rectangle. Cela contraste avec la fonctionGDI Rectangle , qui prend des arguments qui spécifient 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 rouges, vertes et bleues habituelles ; le premier paramètre est la valeur alpha, qui spécifie l’étendue dans laquelle la couleur dessinée est fusionnée avec la couleur d’arrière-plan.

Construction de régions

GDI fournit plusieurs fonctions pour la création de régions : CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn et CreatePolygonRgn. Vous pouvez vous 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 d’objet Rect et un autre constructeur qui reçoit l’adresse d’un objet GraphicsPath . Si vous souhaitez 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 d’accès ou une autre région. Une fonctionnalité intéressante 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. En outre, un objet GraphicsPath n’est pas détruit lorsque son adresse est passée en tant qu’argument à une méthode Union ou Intersect. Vous pouvez donc utiliser un chemin d’accès donné comme bloc de construction 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);