Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :

Nono40.developpez.com
Le petit coin du web de Nono40
SOURCES TESTS DELPHI WIN32 AUTOMATISMES DELPHI .NET QUICK-REPORT
Retour à l'accueil
69 - KALÉIDOSCOPE : SCANLINE EN ASSEMBLEUR

PRÉSENTATION : Utilisation de la propriété ScanLine en assembleur
ZIP : Téléchargez le zip APERÇUS : -1.jpg- -2.jpg-

NOTES : Petite démo pour montrer le principe d'utilisation de TBitMap.ScanLine.
Une fois n'est pas coutume, pour palier au fait que les aperçus ne sont que des images fixes... Voici la version compilée du programme : source0069.exe.zip [200Ko]
Ce programme montre aussi un exemple simple de thread auxiliaire afin de rendre fluide une animation sans avoir recours à DoubleBuffered qui ralenti énormément les animations.
L'utilisation de GetTickCount permet d'avoir une vitesse d'animation à peu près constante suivant les PC.

CODE :
Unit Unit2;
//
// Sujet : Démo de l'utilisation de ScanLine pour le dessin rapide
//         d'un BitMap. Le dessin est réalisé en assembleur afin
//         d'en agmenter aussi la vitesse.
//         C'est aussi un petit exemple de thread de dessin.
//
// Par Nono40 : http://nono40.developpez.com   http://nono40.fr.st
//              mailTo:nono40@fr.st
//
// Le 11/11/2003
//
// Cette Unité contient le Thread de dessin
//

Interface

Uses
  Windows,Classes,Graphics;

Type
  // Définition d'un classe personnalisée simple de thread
  TMonThread = Class(TThread)
  Private
    { Déclarations privées }
    BitMap:TBitMap;
  Protected
    Procedure Execute; override;
    Procedure MAJForm;
  End;

Const
  // Nombre de points-1
  MaxPoints=19999;

  // Taille du dessin en pixels
  Taille   =600;

  // Constante de centrage des points dans l'image. Les points
  // sont calculés dans un repère centré au milieu de l'image
  // Centrage est l'offset en octets du point milieu du bitmap
  // par rapport au coin bas gauche.
  Centrage =((Taille Div 2)*Taille+Taille Div 2)*4;

Implementation

Uses Unit1,Math,SysUtils;

Var
  // Ces trois tableaux contiennent les couleurs et positions
  // des points. En l'indexage d'un tableau d'élément de 4 octets
  // étant plus facile en assembleur, c'est pour cela que la
  // structure est choisie sur trois tableaux séparés et non
  // sur un tableau d'enregistrements.
  LesPointsC:Array[0..MaxPoints]Of TColor;
  LesPointsX:Array[0..MaxPoints]Of Single;
  LesPointsY:Array[0..MaxPoints]Of Single;

  // Cette variable va contenir le point en cours. C'est à dire
  // celui qui est le plus "loin".
  LePoint   :Integer;

  // Tableau contenant la liste précalculée des couleurs des points.
  LesCouleurs:Array[0..128*6-1]Of TColor;

  // Variables de mémorisation du type d'animation en cours
  TypeAnim     :Integer;
  PasAnim      :Integer;

Procedure TMonThread.Execute;
Var
  i,j          :Integer;  // Indexs de travail...
  x            :Integer;  // Position X du point en cours de dessin
  y            :Integer;  // Position Y du point en cours de dessin
  Vide         :Single;   // Variable temporaire
  Pas          :Single;   // Pas de zoom exponentiel
  Tick         :DWord;    // Chronomatrage pour la vitesse de dessin
  PtrBmp       :Pointer;  // Pointeur vers le début du bitmap
  Delta        :Integer;  // Nombre d'octets entre deux lignes de ScanLine[]
  Fin          :Integer;  // Variable contenant la fin du tableau
  IndexCouleur :Integer;  // Index dans le tableau des couleurs

  // Incrémentation du pas d'animation et
  // choix de l'animation suivante.
  Procedure TestAnimSuivante(Maxi:Integer);
  Begin
    Inc(PasAnim);
    If PasAnim>Maxi Then
    Begin
      PasAnim:=0;
      Inc(TypeAnim);
      If TypeAnim>8 Then TypeAnim:=1;
    End;
  End;

Begin
  // Création du bitmap de travail avec les propriétés voulues
  // Le format pf32bit permet d'accéder aux pixels comme un
  // tableau d'entiers.
  BitMap:=TBitMap.Create;
  BitMap.Width  := Taille;
  BitMap.Height := Taille;
  BitMap.PixelFormat := pf32Bit;
  BitMap.Canvas.Brush.Color := clBlack;
  BitMap.Canvas.Brush.Style := bsSolid;

  // Préremplissage du tableau des couleurs
  For i:=0 To 127 Do LesCouleurs[    i]:=$FE0000+(i*2)Shl 8;
  For i:=0 To 127 Do LesCouleurs[128+i]:=$FEFE00-(i*2)Shl 16;
  For i:=0 To 127 Do LesCouleurs[256+i]:=$00FE00+(i*2);
  For i:=0 To 127 Do LesCouleurs[384+i]:=$00FEFE-(i*2)Shl 8;
  For i:=0 To 127 Do LesCouleurs[512+i]:=$0000FE+(i*2)Shl 16;
  For i:=0 To 127 Do LesCouleurs[640+i]:=$FE00FE-(i*2);

  // Effacement des points
  For i:=0 To MaxPoints Do
  Begin
    LesPointsC[i] := clBlack;
    LesPointsX[i] := 0;
    LesPointsY[i] := 0;
  End;
  LePoint :=0;

  // Le premier point en mémoire d'un bitmap est le premier
  // point de la dernière ligne.
  PtrBmp  :=BitMap.ScanLine[Taille-1];

  // Calcul de la distance en octets entre deux lignes
  // et du sens de cette différence, ceci s'ffranchit
  // des alignement et du sens "Haut-Bas"/"Bas-haut" du bitmap
  Delta   :=Integer(BitMap.ScanLine[Taille-2])-Integer(PtrBmp);

  // Pas de zoom pour la simulation d'avance des points
  Pas   := Power(9,1/(Maxpoints+1));

  // Memo pour synchro sur le nombre de Tick
  Tick  := 0;

  // Mémoire pour CMOV ( ne peut pas faire sur une valeur immédiate )
  Fin   := MaxPoints;

  // Couleur en cours en bas du tableau
  IndexCouleur := 0;

  // Sélection de la première animation
  TypeAnim := 7;
  PasAnim  := 0;

  While Not Terminated Do
  Begin
    If (GetTickCount-Tick)>=20 Then // Une image tous les 20ms soit 50 Images/Seconde
    Begin
      // Mémorisation de l'heure de départ
      Tick:=GetTickCount;

      // Calcul de 100 nouveaux points
      // La liste des points étant FIFO, les points les plus
      // vieux sont donc remplacés
      For j:=1 To 5 Do
      Begin
        // Ajout d'un groupe de points
        For i:=1 To 20 Do
        Begin
          // Point suivant
          Inc(LePoint);
          If LePoint>MaxPoints Then LePoint:=0;

          // Couleur du point
          LesPointsC[LePoint] := LesCouleurs[IndexCouleur];

          // Sélection du calcul en fonction du type d'animation en cours
          Case TypeAnim Of
            1 :Begin // Cercle de plus en plus rapide
                 Vide :=(PasAnim*0.00200)*Pi*PasAnim/(MaxPoints+1);
                 LesPointsX[LePoint] := Cos(Vide)*50;
                 LesPointsY[LePoint] := Sin(Vide)*50;
                 TestAnimSuivante(20000);
               End;
            2 :Begin // Cercle de moins en moins rapide
                 Vide := Sqr(20000-PasAnim)*0.00200*Pi/(MaxPoints+1);
                 LesPointsX[LePoint] := Cos(Vide)*50;
                 LesPointsY[LePoint] := Sin(Vide)*50;
                 TestAnimSuivante(20000);
               End;
            3 :Begin // Bande ondulée
                 LesPointsX[LePoint] := Cos(256*Pi*PasAnim/(MaxPoints+1))*50;
                 LesPointsY[LePoint] := Sin(  4*Pi*PasAnim/(MaxPoints+1))*50;
                 TestAnimSuivante(50000);
               End;
            4 :Begin // Ressort
                 Vide := PasAnim+2000*Sin(PasAnim);
                 LesPointsX[LePoint] := Cos( 6*Pi*Vide/(MaxPoints+1))*50;
                 LesPointsY[LePoint] := Sin( 6*Pi*Vide/(MaxPoints+1))*50;
                 TestAnimSuivante(60000);
               End;
            5 :Begin // Carré
                 Vide :=Sin(20*Pi*PasAnim/(MaxPoints+1))*50;
                 Case PasAnim And 3 Of
                   0Begin
                        LesPointsX[LePoint] := 50;
                        LesPointsY[LePoint] := Vide;
                      End;
                   1Begin
                        LesPointsX[LePoint] := -Vide;
                        LesPointsY[LePoint] := -50;
                      End;
                   2Begin
                        LesPointsX[LePoint] := -50;
                        LesPointsY[LePoint] := -Vide;
                      End;
                   3Begin
                        LesPointsX[LePoint] := Vide;
                        LesPointsY[LePoint] := 50;
                      End;
                 End;
                 TestAnimSuivante(50000);
               End;
            6 :Begin // Coeur
                 Case PasAnim Mod 200 Of
                    0..49 : Begin
                              Vide := (PasAnim Mod 200)+0;
                              LesPointsX[LePoint] :=-Cos( Pi*Vide/40 )*25+25;
                              LesPointsY[LePoint] := Sin( Pi*Vide/40 )*25+25;
                            End;
                   50..99 : Begin
                              Vide := (PasAnim Mod 200)-50;
                              LesPointsX[LePoint] := Cos( Pi*Vide/40 )*25-25;
                              LesPointsY[LePoint] := Sin( Pi*Vide/40 )*25+25;
                            End;
                  100..149Begin
                              Vide := (PasAnim Mod 200)-90;
                              LesPointsX[LePoint] := Vide*50/57 -52;
                              LesPointsY[LePoint] :=-Vide*75/57 +20.5;
                            End;
                  150..199Begin
                              Vide := (PasAnim Mod 200)-140;
                              LesPointsX[LePoint] :=-Vide*50/57 +52;
                              LesPointsY[LePoint] :=-Vide*75/57 +20.5;
                            End;
                 End;
                 TestAnimSuivante(40000);
               End;
            7 :Begin // Sinusoide sur cylindre et rotatif
                 Vide := Sin(Pi*PasAnim/500)+PasAnim/2000;
                 LesPointsX[LePoint] := Cos( 0.4*Pi*Vide )*50;
                 LesPointsY[LePoint] := Sin( 0.4*Pi*Vide )*50;
                 TestAnimSuivante(40000);
               End;
            8 :Begin // Spirale
                 Vide :=240*Pi*PasAnim/(MaxPoints+1);
                 LesPointsX[LePoint] := Cos(Vide)*10+40*Cos(Vide/40);
                 LesPointsY[LePoint] := Sin(Vide)*10+40*Sin(Vide/40);
                 TestAnimSuivante(40000);
               End;
          Else
            TypeAnim:=1;
          End;
        End;

        // Couleur suivante à la fin du groupe de points
        Inc(IndexCouleur);
        If IndexCouleur>High(LesCouleurs) Then IndexCouleur:=0;
      End;

      // Effacement du bitmap
      Bitmap.Canvas.FillRect(BitMap.Canvas.ClipRect);

      // Dessin des points dans le bitmap
      Asm
        PUSH  EDI
        PUSH  EBX
        PUSH  ESI

        MOV   EDX,LePoint          // On commence par le point du fond
        MOV   EDI,PtrBmp           // Pointeur vers le BitMap
        MOV   EBX,Delta            // Décalage d'une ligne à l'autre

        FLD   Pas                  // Lecture du pas multiplicatif
        FLD1                       // Le coeff commence à 1

@@Boucle:
        FLD   DWord Ptr LesPointsY[EDX*4]
        FMUL  ST(0),ST(1)          // Calcul de Y*Coeff
        FISTP Y
        MOV   EAX,Y                // Chargement de Y

        CMP   EAX,-(Taille/2)      // Tests de débordement
        JL    @@HorsBmp
        CMP   EAX, (Taille/2)-4
        JG    @@HorsBmp

        FLD   DWord Ptr LesPointsX[EDX*4]
        FMUL  ST(0),ST(1)          // Calcul de X*Coeff
        FISTP X

        CMP   X,-(Taille/2)        // Tests de débordement
        JL    @@HorsBmp
        CMP   X, (Taille/2)-4
        JG    @@HorsBmp

        ADD   EAX,Taille/2         // Centrage Y
        IMUL  EAX,EBX              // Offset de la ligne ( coordonnée "Y" )
        LEA   ESI,[EDI+EAX]        // ESI va pointer sur le début de la ligne

        MOV   EAX,X                // Lecture de la coordonnée "X"

                                   // Lecture de la couleur du point
        MOV   ECX,DWord ptr LesPointsC[EDX*4]
                                   // Ecriture du point sous forme
                                   // de 12 pixels
        MOV   DWord Ptr [ESI+EAX*44+Taille*2],ECX //    **
        MOV   DWord Ptr [ESI+EAX*48+Taille*2],ECX
        ADD   ESI,EBX              // Ligne suivante
        MOV   DWord Ptr [ESI+EAX*40+Taille*2],ECX //   ****
        MOV   DWord Ptr [ESI+EAX*44+Taille*2],ECX
        MOV   DWord Ptr [ESI+EAX*48+Taille*2],ECX
        MOV   DWord Ptr [ESI+EAX*4+12+Taille*2],ECX
        ADD   ESI,EBX              // Ligne suivante
        MOV   DWord Ptr [ESI+EAX*40+Taille*2],ECX //   ****
        MOV   DWord Ptr [ESI+EAX*44+Taille*2],ECX
        MOV   DWord Ptr [ESI+EAX*48+Taille*2],ECX
        MOV   DWord Ptr [ESI+EAX*4+12+Taille*2],ECX
        ADD   ESI,EBX              // Ligne suivante
        MOV   DWord Ptr [ESI+EAX*44+Taille*2],ECX //    **
        MOV   DWord Ptr [ESI+EAX*48+Taille*2],ECX

@@HorsBMP:
        FMUL  ST(0),ST(1)          // Coeff:=Coeff*Pas

        DEC   EDX                  // Point suivant
        CMOVS EDX,Fin              // Retour en fin de tableau

        CMP   EDX,LePoint          // Test de fin de boucle
        JNE   @@Boucle

        FSTP  Vide                 // Dépilage de Coeff en fin de boucle
        FSTP  Vide                 // Dépilage de Pas en fin de boucle

        POP   ESI
        POP   EBX
        POP   EDI
      
End;

      // Mise à jour de la fiche
      Synchronize(MAJForm);
    End;
  End;

  BitMap.Free;
End;

Procedure TMonThread.MAJForm;
Begin
  // La mise à jour est un simple Draw sur l'image.
  Form1.Image1.Canvas.Draw(0,0,BitMap);
End;

end.

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 © 2003 Bruno Guérangé. 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 -