Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :


Des types de ligne personnalisés

Date de publication : 11 novembre 2000 , Date de mise à jour : 06 novembre 2005

Par Maxence Delannoy (http://mdelannoy.developpez.com/)
 

Voici le deuxième article d'une série sur la programmation graphique sous Delphi.


I. Introduction
II. Le principe
III. Le code
IV. Un peu d'explication
V. Le revers de la médaille


I. Introduction

Sous Windows 95 et 98, on ne dispose que de quelques styles de crayon (psDot, psDash...), alors que sous NT, on peut définir son propre motif (fonction ExtCreatePen de l'API Window). Si vous avez besoin de types de ligne personnalisés pour votre application qui soient portables sur les différentes plateformes Microsoft, cet article est fait pour vous...

Fig. 1 - Des lignes tracées en pointillés.

II. Le principe

Windows fournit une fonction méconnue nommée LineDDA. Cette fonction est prototypée de la manière suivante dans Windows.pas :
function LineDDA(p1, p2, p3, p4: Integer; p5: TFNLineDDAProc; p6: LPARAM): BOOL; stdcall;
Les 4 premiers paramètres correspondent aux coordonnées x et y de deux points de l'écran. En cinquième paramètre, on doit passer l'adresse d'une fonction d'appel en retour (Callback) qui doit suivre le prototype suivant :
procedure LineDDAProc(x, y: Integer; Data: LParam); stdcall;
En fait, quand on lance la fonction LineDDA, cette dernière calcule les coordonnées de tous les pixels qu'il faudrait allumer pour tracer une ligne du point de départ de coordonnées (p1, p2) au point de coordonnées (p3, p4). Pour chaque pixel, elle appelle la fonction d'appel en retour en lui passant les coordonnées du pixel en argument, ainsi qu'un pointeur (p6) sur une zone de données. Si dans cette fonction, on change la couleur du pixel courant (avec Canvas.Pixels[x,y] := Canvas.pen.Color par exemple), on obtient une ligne continue. Si on ne modifie par contre qu'un pixel sur 5, on obtient un motif discontinu.


III. Le code

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TLinePattern = array[0..11] of Byte;
  PLinePattern = ^TLinePattern;

  TDDAData = record
    Canvas: TCanvas;
    Pos: Integer;
    Pattern: PLinePattern;
  end;
  PDDAData = ^TDDAData;

  TForm1 = class(TForm)
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

  procedure LineDDAProc(X, Y: Integer; Data: LParam); stdcall;

var
  Drawing: Boolean;
  FirstPoint: TPoint;
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
const
  LP: TLinePattern = (1, 1, 1,
                      0, 0, 0,
                      1, 1, 1,
                      0, 0, 0);
var
  i: Integer;
  Data: TDDAData;
begin
  if Drawing then
  begin
    Data.Canvas := Canvas;
    Data.Pos := 0;
    Data.Pattern := @LP;
    LineDDA(FirstPoint.X, FirstPoint.Y, X, Y, @LineDDAProc, LongInt(@Data));
    Drawing := False;
  end
  else
  begin
    FirstPoint := Point(X, Y);
    Drawing := True;
  end;
end;

procedure LineDDAProc(X, Y: Integer; Data: LParam);
begin
  if PDDAData(Data).Pattern[PDDAData(Data).Pos mod SizeOf(TLinePattern)] = 1
  then PDDAData(Data).Canvas.Pixels[X, Y] := PDDAData(Data).Canvas.Pen.Color;
  Inc(PDDAData(Data).Pos);
end;

end.
Pour télécharger le code source (Delphi 2.0), cliquez ici ou cliquez ici.


IV. Un peu d'explication

On commence par définir un type nommé TLinePattern qui va nous permettre de stocker nos différents types de ligne. Il s'agit en fait d'un tableau de douze octets (Rien n'empêche de créer des motifs plus long ou plus court. On peut même utiliser un tableau dynamique). Si l'octet contient la valeur 1, on trace le pixel. Si il est égal à 0, on ne fait rien. La variable LP correspond à un motif de ligne constitué d'un tiret et d'un espace de trois pixels chacun, qui se répéte tout le long de la ligne.

Dans le paramètre Data de la fonction LineDDA, on passe l'adresse d'un enregistrement de type TDDAData, qui contient une référence vers le canevas sur lequel on veut tracer la ligne, une variable Pos qui nous permet de déteminer notre position dans le motif, ainsi qu'un pointeur sur une variable ou une constante du type TLinePattern qui indique le motif à tracer.

Attention à ne pas oublier la directive stdcall dans l'entête de la fonction LineDDAProc. Elle force Delphi à utiliser une convention d'appel compatible avec les routines de l'API Windows.


V. Le revers de la médaille

Cette technique est intéressante, mais elle présente un inconvénient de taille : elle est très lente. Cela est du principalement à l'utilisation de la propriété Pixels (qu'on peut remplacer par un appel à l'API SetPixelV pour gagner quelques millisecondes) et aux nombreux appels de procédures engendrés par les fonctions de rappel. Dans un prochain article, nous implémenterons nous même le calcul des différents points d'une ligne pour supprimer ce deuxième point.




Valid XHTML 1.1!Valid CSS!

Copyright © 2000 Maxence Delannoy. 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.

Responsables bénévoles de la rubrique Delphi : Gilles Vasseur - Alcatîz -