Page 1 sur 1

Taches de Martin

Publié : sam. 12/juin/2004 6:02
par filperj
Je suis tombé sur l'algo des taches de Martin en fouinant dans l'excellent site d'Opie Pécheux: http://opecheux.free.fr/

C'est tout simple: l'écran est représenté par un tableau bidimensionnel d'octets (en mode 256 couleurs). A chaque boucle d'affichage, on parcourt tout le tableau et, pour chaque pixel, on fait la moyenne entre les valeurs de ses 4 voisins (dessus, dessous, gauche, droite), on ajoute 1 à cette moyenne et on attribue la valeur obtenue au pixel.

Autrement dit, à chaque boucle, pour chaque pixel:
pix(x,y)=(pix(x,y-1)+pix(x-1,y)+pix(x+1,y)+pix(x,y+1))/4+1

Enfantin, non? Et ça fait ça:
(Il faut activer l'assembleur en ligne dans les options de compilation - l'assembleur n'est pas vraiment de l'optimisation, c'est plutôt que j'ai eu la flemme de traduire le code originel en PB)

Code : Tout sélectionner


Global _virtuscr.l

#largeur=640 : #hauteur=480   ;dimensions de l'écran
!largeur=640                  ;les mêmes pour Fasm
!hauteur=480


Enumeration
   #rougebleujaune
   #nivgris
   #defaut
EndEnumeration

;#modepalette=#rougebleujaune
#modepalette=#nivgris
;#modepalette=#defaut

CompilerIf #modepalette=#defaut
   
   Procedure palettiser()
   EndProcedure
   
  CompilerElse
   
   Procedure palettiser()
      
      InitPalette()
      
      CreatePalette(0)
      
      CompilerSelect #modepalette
         
         CompilerCase #rougebleujaune
            
            For t=0 To 63
               SetPaletteColor(t,RGB(t*4,0,0))
               SetPaletteColor(t+64,RGB((63-t)*4,0,t*4))
               SetPaletteColor(t+128,RGB(t*4,t*4,(63-t)*4))
               SetPaletteColor(t+192,RGB(255,255,t*4))
            Next
            
         CompilerCase #nivgris
         
            For t=0 To 255
               SetPaletteColor(t,RGB(t,t,t))
            Next
         
      CompilerEndSelect
      
      DisplayPalette(0)
      
   EndProcedure
   
CompilerEndIf




Procedure onattend()
   
   Repeat
      Delay(80)
      FlipBuffers()
   Until IsScreenActive()
   
EndProcedure



Procedure rendu()
   
   scraoutpout=ScreenOutput()
   If scraoutpout : If StartDrawing(scraoutpout)
      
      dbuff=DrawingBuffer()
      pitch=DrawingBufferPitch()
      
      If pitch=#largeur
         
         CopyMemory(_virtuscr,dbuff,#largeur*#hauteur)
         
        Else
         
         For ligne=0 To #hauteur-1
            CopyMemory(_virtuscr+ligne*#largeur,dbuff+ligne*pitch,#largeur)
         Next
         
      EndIf
      
   StopDrawing() : EndIf : EndIf
   
EndProcedure




Enumeration
   #bordsnoirs
   #bordsjoints
EndEnumeration

#modedesbords=#bordsnoirs
;#modedesbords=#bordsjoints

Procedure martinet()
   
   DisableDebugger   ;le débogueur fout le bordel dans les "LOOP"
   
   CompilerSelect #modedesbords
      
      CompilerCase #bordsjoints
         
         MOV edi,_virtuscr
         XOR ebx,ebx
         CLD
         MOVZX ax,byte[edi+largeur*(hauteur-1)]
         MOV bl,[edi+largeur*hauteur-1]
         ADD ax,bx
         MOV bl,[edi+1]
         ADD ax,bx
         MOV bl,[edi+largeur]
         ADD ax,bx
         SHR ax,2
         INC al
         !stosb
         MOV ecx,largeur-1
         lign0:
           MOVZX ax,byte[edi+largeur*(hauteur-1)]
           MOV bl,[edi-1]
           ADD ax,bx
           MOV bl,[edi+1]
           ADD ax,bx
           MOV bl,[edi+largeur]
           ADD ax,bx
           SHR ax,2
           INC al
           !stosb
           LOOP l_lign0
         MOV ecx,largeur*(hauteur-2)
         centrecr:
           MOVZX ax,byte[edi-largeur]
           MOV bl,[edi-1]
           ADD ax,bx
           MOV bl,[edi+1]
           ADD ax,bx
           MOV bl,[edi+largeur]
           ADD ax,bx
           SHR ax,2
           INC al
           !stosb
           LOOP l_centrecr
         MOV ecx,largeur-1
         derlign:
           MOVZX ax,byte[edi-largeur]
           MOV bl,[edi-1]
           ADD ax,bx
           MOV bl,[edi+1]
           ADD ax,bx
           MOV bl,[edi-largeur*(hauteur-1)]
           ADD ax,bx
           SHR ax,2
           INC al
           !stosb
           LOOP l_derlign
         MOVZX ax,byte[edi-largeur]
         MOV bl,[edi-1]
         ADD ax,bx
         MOV bl,[edi+1-largeur*hauteur]
         ADD ax,bx
         MOV bl,[edi-largeur*(hauteur-1)]
         ADD ax,bx
         SHR ax,2
         INC al
         !stosb
         
      CompilerCase #bordsnoirs
         
         MOV edi,_virtuscr
         ADD edi,largeur+1
         MOV edx,hauteur-2    
         XOR ebx,ebx
         CLD
         bclecr:
           MOV ecx,largeur-2
           bcllign:
             MOVZX ax,byte[edi-largeur]
             MOV bl,[edi-1]
             ADD ax,bx
             MOV bl,[edi+1]
             ADD ax,bx
             MOV bl,[edi+largeur]
             ADD ax,bx
             SHR ax,2
             INC al
             !stosb
             LOOP l_bcllign
           ADD edi,2
           DEC edx
           JNZ l_bclecr
         
   CompilerEndSelect
   
   EnableDebugger
   
EndProcedure



;-ZHE PROG



If InitKeyboard() And InitSprite() And OpenScreen(#largeur,#hauteur,8,"Taches de Martin")

   palettiser()
   
   _virtuscr=AllocateMemory(#largeur*#hauteur)
   
   SetFrameRate(25)
   
   Repeat
      
      martinet()
      
      rendu()
      
      ExamineKeyboard()
      
      FlipBuffers()
      
      If IsScreenActive()=0
         onattend()
      EndIf
      
   Until KeyboardReleased(#pb_key_escape)

   
  Else
   
   MessageRequester("Ah ben ça marche pas.","Impossible d'initialiser DirectX7")
   
EndIf

Publié : sam. 12/juin/2004 9:36
par comtois
C'est joli

J'ai essayé en mettant un peu de couleur

Code : Tout sélectionner

  For t=0 To 255 
    SetPaletteColor(t,RGB(255-t,255-t,0)) 
  Next 
ça pourrait faire un joli screensaver :)

Publié : sam. 12/juin/2004 11:59
par filperj
Ben et tout mes compilerif, alors? :lol:
Il suffit de changer la valeur de la constante #modepalette.
Esaie de lui donner la valeur "#defaut", chez moi ça donne un truc kitchissime.

Et au fait, un tableau d'octets ça peut aussi représenter une heightmap, non?
Ca pourrai être rigolo :D

Publié : sam. 12/juin/2004 12:15
par comtois
ah oui je n'avais pas vu , c'est encore mieux comme ça :)

quand tu parles de heightmap c'est une image ?

J'allais te le dire ,ça serait bien de le faire avec une image pour voir :)

Publié : sam. 12/juin/2004 15:40
par fweil
é tt mignon ce truc ...

Publié : sam. 12/juin/2004 23:04
par Le Soldat Inconnu
c'est pas mal du out, extra, je dirais même, j'adore l'effet que ça produit :)

Publié : dim. 13/juin/2004 11:49
par Backup
a mi-chemin entre Magma et comportement Chaotique, j'adore !

Bravo .. :D (un extincteur de plus ?? ) :D

Publié : dim. 13/juin/2004 16:46
par filperj
C'est à Martin qu'il faut dire Bravo!
Bon tout à l'heure j'essaierai d'initialiser le tableau avec une image.

Publié : dim. 13/juin/2004 22:02
par filperj
Bon voilà, j'ai initialisé avec une image.
Mais en fait, Bah... Aucun interrêt!
L'image se dissout rapidement dans la mousse, et on en entend plus parler :cry:

Code : Tout sélectionner


UseJPEGImageDecoder() : UsePNGImageDecoder()

Global _virtuscr.l,_image.s

#largeur=640 : #hauteur=480   ;dimensions de l'écran
!largeur=640                  ;les mêmes pour Fasm
!hauteur=480


Enumeration
   #rougebleujaune
   #nivgris
   #defaut
   #bleurose
EndEnumeration

;#modepalette=#rougebleujaune
#modepalette=#nivgris
;#modepalette=#defaut
;#modepalette=#bleurose

CompilerIf #modepalette=#defaut
   
   Procedure palettiser()
   
      InitPalette()
      If LoadPalette(0,_image)
         DisplayPalette(0)
      EndIf
      
   EndProcedure
   
  CompilerElse
   
   Procedure palettiser()
      
      InitPalette()
      
      If LoadPalette(0,_image)
         DisplayPalette(0)
         ProcedureReturn
      EndIf
      
      CreatePalette(0)
      
      DisplayPalette(0)
      
      CompilerSelect #modepalette
         
         CompilerCase #rougebleujaune
            
            For t=0 To 63
               SetPaletteColor(t,RGB(t*4,0,0))
               SetPaletteColor(t+64,RGB((63-t)*4,0,t*4))
               SetPaletteColor(t+128,RGB(t*4,t*4,(63-t)*4))
               SetPaletteColor(t+192,RGB(255,255,t*4))
            Next
            
         CompilerCase #nivgris
         
            For t=0 To 255
               SetPaletteColor(t,RGB(t,t,t))
            Next
         
         CompilerCase #bleurose
            
            For t=0 To 255
               SetPaletteColor(t,RGB(t,t/2,t/2+128))
            Next
            
      CompilerEndSelect
      
      DisplayPalette(0)
      
   EndProcedure
   
CompilerEndIf




Procedure onattend()
   
   Repeat
      Delay(80)
      FlipBuffers()
   Until IsScreenActive()
   
EndProcedure



; Procedure rendu()
;    
;    scraoutpout=ScreenOutput()
;    If scraoutpout : If StartDrawing(scraoutpout)
;       
;       dbuff=DrawingBuffer()
;       pitch=DrawingBufferPitch()
;       
;       If pitch=#largeur
;          
;          CopyMemory(_virtuscr,dbuff,#largeur*#hauteur)
;          
;         Else
;          
;          For ligne=0 To #hauteur-1
;             CopyMemory(_virtuscr+ligne*#largeur,dbuff+ligne*pitch,#largeur)
;          Next
;          
;       EndIf
;       
;    StopDrawing() : EndIf : EndIf
;    
; EndProcedure




Enumeration
   #bordsnoirs
   #bordsjoints
EndEnumeration

#modedesbords=#bordsnoirs
;#modedesbords=#bordsjoints

Procedure martinet()
   
   DisableDebugger   ;le débogueur fout le bordel dans les "LOOP"
   
   CompilerSelect #modedesbords
      
      CompilerCase #bordsjoints
         
         MOV edi,_virtuscr
         XOR ebx,ebx
         CLD
         MOVZX ax,byte[edi+largeur*(hauteur-1)]
         MOV bl,[edi+largeur*hauteur-1]
         ADD ax,bx
         MOV bl,[edi+1]
         ADD ax,bx
         MOV bl,[edi+largeur]
         ADD ax,bx
         SHR ax,2
         INC al
         !stosb
         MOV ecx,largeur-1
         lign0:
           MOVZX ax,byte[edi+largeur*(hauteur-1)]
           MOV bl,[edi-1]
           ADD ax,bx
           MOV bl,[edi+1]
           ADD ax,bx
           MOV bl,[edi+largeur]
           ADD ax,bx
           SHR ax,2
           INC al
           !stosb
           LOOP l_lign0
         MOV ecx,largeur*(hauteur-2)
         centrecr:
           MOVZX ax,byte[edi-largeur]
           MOV bl,[edi-1]
           ADD ax,bx
           MOV bl,[edi+1]
           ADD ax,bx
           MOV bl,[edi+largeur]
           ADD ax,bx
           SHR ax,2
           INC al
           !stosb
           LOOP l_centrecr
         MOV ecx,largeur-1
         derlign:
           MOVZX ax,byte[edi-largeur]
           MOV bl,[edi-1]
           ADD ax,bx
           MOV bl,[edi+1]
           ADD ax,bx
           MOV bl,[edi-largeur*(hauteur-1)]
           ADD ax,bx
           SHR ax,2
           INC al
           !stosb
           LOOP l_derlign
         MOVZX ax,byte[edi-largeur]
         MOV bl,[edi-1]
         ADD ax,bx
         MOV bl,[edi+1-largeur*hauteur]
         ADD ax,bx
         MOV bl,[edi-largeur*(hauteur-1)]
         ADD ax,bx
         SHR ax,2
         INC al
         !stosb
         
      CompilerCase #bordsnoirs
         
         MOV edi,_virtuscr
         ADD edi,largeur+1
         MOV edx,hauteur-2    
         XOR ebx,ebx
         CLD
         bclecr:
           MOV ecx,largeur-2
           bcllign:
             MOVZX ax,byte[edi-largeur]
             MOV bl,[edi-1]
             ADD ax,bx
             MOV bl,[edi+1]
             ADD ax,bx
             MOV bl,[edi+largeur]
             ADD ax,bx
             SHR ax,2
             INC al
             !stosb
             LOOP l_bcllign
           ADD edi,2
           DEC edx
           JNZ l_bclecr
         
   CompilerEndSelect
   
   EnableDebugger
   
EndProcedure


Procedure choisimage()
   
   _image=OpenFileRequester("Martin","","fichiers images|*.bmp;*.dib;*.png;*.jpg;*.jpeg",0)
   If _image="" Or LoadImage(0,_image)=0 : End : EndIf
   
EndProcedure





Procedure initecran()
   
   CreateSprite(0,#largeur,#hauteur,#pb_sprite_memory)
   
   StartDrawing(SpriteOutput(0))
      
      _virtuscr=DrawingBuffer()
      DrawImage(UseImage(0),0,0,#largeur,#hauteur)
      
   StopDrawing()
   
   FreeImage(0)
   
EndProcedure







;-ZHE PROG

choisimage()


If InitKeyboard() And InitSprite() And OpenScreen(#largeur,#hauteur,8,"Taches de Martin")

   palettiser()
   
   INitecran()
   
   SetFrameRate(25)
   
   Repeat
      
      martinet()
      
      ;rendu()
      DisplaySprite(0,0,0)
      
      ExamineKeyboard()
      
      FlipBuffers()
      
      If IsScreenActive()=0
         onattend()
      EndIf
      
   Until KeyboardReleased(#pb_key_escape)

   
  Else
   
   MessageRequester("Ah ben ça marche pas.","Impossible d'initialiser DirectX7")
   
EndIf
Bon tant pis.

Publié : jeu. 17/juin/2004 21:28
par fweil
Ben moi je vous ai fait ça pour broder un peu autour des tâches et de l'assembleur ...

Code : Tout sélectionner

;
; Small ASM tutorial
; Petit TUT ASM
;
; F.Weil 20040617
;
; This listing is a workshop about ASM insertion in PureBasic code. Nothing exhaustive but a possible first basis.
;
; The workshop is based on 'les taches de Martin', meaning each point of an image is set according to the average of it's neighbors.
; A limited color palette is necessary to render appropriate effect.
;
; I propose to use a regular 32 bits depth for the screen and to solve this by calculating the final color of each point.
;
; Focus is made on how to address the drawing buffer directly.
;
; Purpose of this workshop is not to show that it is possible to do much better than PureBasic as it demonstrates the opposite.
; Only in some cases using ASM will help, but most of the time not. This workshop shows how to code in PureBasic well, and how
; few improvements can be obtained using ASM coding.
;
; For a better user experience, it is possible to parse methods execution by using left / right keyboard arrows, and to change
; drawing area size using NumPad4 / NumPad6
;
; The algorithm name, frames per second obtained and drawing size are displayed in real time.
;================================================================================
; Ce listing a pour but de montrer comment étudier la transposition en ASM de certaines parties de code.
; Rien d'exhaustif mais une prise en main qui peut donner de bonnes bases.
;
; Reprise du principe des taches de Martin
;
; Là j'ai mis l'accent sur la gestion directe du DrawingBuffer
;
; L'algorithme proposé est de même principe que celui d'origine pour les taches de Martin, c'est à dire
; pour chaque point P(X, Y) on associe une couleur calculée à partir de la moyenne des couleurs des 4 points
; voisins + 1
; Soit couleur(P(X, Y)) = (couleur(P1(X, Y - 1)) + couleur(P2(X - 1, Y)) + couleur(P3(X + 1, Y)) + couleur(P4(X, Y + 1))) / 4 + 1
;
; Pour rendre l'effet escompté on doit travailler dans une palette limitée.
;
; Pour faire les choses sans passer par un screen en 8 bits ou autre chose, je prends le mode par défaut de l'écran en 32 bits, et
; je convertis en recalculant couleur
;
; couleur = couleur % 64 + 192
; couleur = couleur << 10 + couleur
;
; Le but de cette démonstration ne consiste pas à montrer des carences de PureBasic, bien au contraire. Il ressort que l'utilisation de l'ASM
; n'apporte que peu d'améliorations si le codage PureBasic est bien conçu par le développeur.
;
; Pour une meilleure expérimentation des méthodes ici, il est possible de naviguer sur les différents types de code en utilisant les
; flèches gauche / droite du clavier et de changer la taille de la zone de dessin avec les touches 4 / 6 du clavier numérique.
;
; Un affichage temps réel de l'algorithme utilisé, du nombre de trames par seconde et de la taille du dessin est effectué.
;

; This enumeration is made to make the different codes to executes easier to access
; Also the codes names are placed in strings to display stats
;
; Cette énumération est mise en place pour rendre l'accès aux différents algorithmes plus aisés.
; Les noms des codes à exécuter sont également placés dans un tableau de chaînes pour l'affichage des stats.

;
; Here is a chart of experimental results (inFPS) I found on my design PC
; Voici un tableau des résultats expérimentaux (en FPS) obtenus sur mon PC de développement
;
; FlipBuffers(1)                        128x128    256x256    320x240    480x360    640x480    800x600
; PureBasic_Regular_Code             4              2               1              -                -              -
; PureBasic_Better_Code             60             30             30            21              10             8
; PureBasic_Optimized_Code       60             30             30            22              10             8
; PureBasic_LowLevel_Code        60             30             30            22              10             8
; PureBasic_Chewed_Code          60             30             30            19                9             7
; ASM_Code                               60             30             30            23              10             8
; PureBasic_LowLevel_Code2       60             59             59            45              20           15
; ASM_LowLevel_Code2              60             60             59             50              22           16
;       
; FlipBuffers(0)                        128x128    256x256    320x240    480x360    640x480    800x600
; PureBasic_Regular_Code             5              2               1              -                -              -
; PureBasic_Better_Code            260            55             46            21              12              8
; PureBasic_Optimized_Code       251            50             49           22               11              8
; PureBasic_LowLevel_Code        251            50             49           22               11              8
; PureBasic_Chewed_Code         216            44             42            19              10              7
; ASM_Code                              255            55             52            23              12              9
; PureBasic_LowLevel_Code2      604          126            100           45              27             17
; ASM_LowLevel_Code2              627          143            113           51              30             20
; 
; Results measured on my 1.2GHz / 32MB graphic PC
; Résultats mesurés sur mon PC 1,2GHz / Graphique 32MO
; 

Enumeration
  #PureBasic_Regular_Code
  #PureBasic_Better_Code
  #PureBasic_Optimized_Code
  #PureBasic_LowLevel_Code
  #PureBasic_Chewed_Code
  #ASM_Code
  #PureBasic_LowLevel_Code2
  #ASM_LowLevel_Code2
EndEnumeration

#LastCode = #ASM_LowLevel_Code2

Global tz.l, FPS.l, NFrames.l, AFrames.l, TFrames.l ; Stats

;
; ResetAll allows to clear the screen's buffers and thearrays used in the program
; ResetAll permet d'effacer les buffers et de remettre les tableaux utilisés à zéro.
Procedure ResetAll()
  FlipBuffers(1)
  ClearScreen(0, 0, 0)
  FlipBuffers(1)
  ClearScreen(0, 0, 0)
  Dim DrawingArray.l(1024, 768)
  Dim DrawingArea.l(1024 * 768)
  tz = ElapsedMilliseconds()
  FPS = 0
  NFrames = 0
  AFrames = 0
  TFrames = 0
EndProcedure

ExecuteCode = #ASM_Code

Dim CodeType.s(10)
Dim DrawingWidth.l(10)
Dim DrawingHeight.l(10)

; A list of code parts names is placed in the DataSection
; Une liste des noms des parties du code est placée en DataSection.
For i = 0 To #LastCode
  Read CodeType(i)
  CodeType(i) + Space(40 - Len(CodeType(i)))
Next

LastSize = 5
WidthHeight = 3

; A list of drawing sizes is placed in DataSection
; Une liste des tailles de zone de dessin est donnée en DataSection
For i = 0 To LastSize
  Read DrawingWidth(i)
  Read DrawingHeight(i)
Next

;
; DrawingArray is a 2 dimensions array to store screen's x, y pixels in some part of the program
; DrawingArea is a single dimension array used in the same purpose in some other parts
;
; DrawinArray est un tableau à 2 dimensions utilisé pour mémoriser les pixels x, y de l'écran dans certaines parties du programme
; DrawingArea est un tableau à une seule dimension utilisé dans le même but, mais dans d'autres parties.
;
Dim DrawingArray.l(1024, 768)
Dim DrawingArea.l(1024 * 768)

; The current drawing width and heigth are set and xTop, yTop, xBottom, yBottom are bounds
; La largeur et la hauteur courante pour le dessin sont initalisées ainsi que les limites xTop, yTop, xBottom, yBottom
DrawingWidth = DrawingWidth(WidthHeight)
DrawingHeight = DrawingHeight(WidthHeight)
ScreenXSize = GetSystemMetrics_(#SM_CXSCREEN)
ScreenYSize = GetSystemMetrics_(#SM_CYSCREEN)
xTop = (ScreenXSize - DrawingWidth) / 2 + 1
yTop = (ScreenYSize - DrawingHeight) / 2 + 1
xBottom = xTop + DrawingWidth - 2
yBottom = yTop + DrawingHeight - 2

;
;
;
  If InitKeyboard() And InitSprite() And OpenScreen(ScreenXSize, ScreenYSize, 32, "Taches de Matin") 
      StartDrawing(ScreenOutput())
        DrawingFont(LoadFont(23, "Verdana", 8, #PB_Font_HighQuality | #PB_Font_Bold))
      StopDrawing()
      Repeat 
        ;
        ; FlipBuffers is used with two possible argument value : 0 without buffer buffer sync which is shorter but
        ; may generate more flickering on the screen, or 1 to wait the buffer ready before to display it.
        ;
        ; Mode change is accesible using up / down arrows
        ;=========================================================================
        ; On utilise la commande FlipBuffers(0) (sans synchronisation plus rapide mais qui peut produire des scintillements
        ; ou FlipBuffers(1) qui attend que le buffer soit prêt avant affichage.
        ;
        ; Le changement de mode se fait en appuyant les flèches haut ou bas.
        ;
        FlipBuffers(FlipBuffers)
        StartDrawing(ScreenOutput())
          Select ExecuteCode
            ;
            ; To start the following code just do what the top comments describe, using Point(x, y) to know a pixel value. Neighbors are
            ; injected in a formula and the given point set using Plot.
            ;=====================================================================================
            ; Pour commencer voici dans une écriture conventionnelle le code correspondant à la spécification donnée en commentaire ci-dessus.
            ;
            ; Pour l'ensemble des points à traiter, on calcule la somme des valeurs des 4 points adjacents que l'on injecte dans une formule.
            ;
            ; Le point courant est ensuite simplement dessiné à la position voulue.
            ;
            Case #PureBasic_Regular_Code
              For x = 1 To DrawingWidth - 1
                For y = 1 To DrawingHeight - 1
                  Value = ((Point(x + xTop, y + yTop - 1) + Point(x + xTop, y + yTop + 1) + Point(x + xTop + 1, y + yTop) + Point(x + xTop - 1, y + yTop)) / 4 + 1) % 64 + 192
                  Color = Value << 10 + Value
                  Plot(x + xTop, y + yTop, Color)
                Next
              Next
            ;
            ; In #PureBasic_Better_Code, we do no more use Point(x, y) but an 2 dimensions array to store points values.
            ;
            ; This array contains elements corresponding to screen pixels.
            ;
            ; Using this makes the listing easy to understand but the performances are much better
            ;=======================================================================================
            ; Dans la version #PureBasic_Better_Code on ne va plus utiliser la fonction Point(x, y) mais un tableau qui permet de mémoriser les
            ; valeurs des points.
            ;
            ; Ce tableau est à deux dimensions et chaque élément x, y du tableau représente fidèlement un point de l'écran
            ;
            ; Par contre le tracé du point courant est fait avec la fonction Plot, dont les performances restent assez bonnes.
            ;
            ; De là l'écriture est encore assez lisible ... et les performances très différentes
            ;
            Case #PureBasic_Better_Code
              For x = 1 To DrawingWidth - 1
                For y = 1 To DrawingHeight - 1
                  Value = ((DrawingArray(x, y - 1) + DrawingArray(x, y + 1) + DrawingArray(x - 1, y) + DrawingArray(x + 1, y)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArray(x, y) = Color
                  Plot(x + xTop, y + yTop, Color)
                Next
              Next
            ;
            ; In #PureBasic_Optimized_Code a single dimension array is used.
            ;
            ; This makes the code a bit more difficult to understand but will make easier later optimization.
            ;=============================================================================
            ; Dans la version #PureBasic_Optimized_Code on utilise un tableau linéaire et non plus une matrice à deux dimensions.
            ;
            ; L'écriture du code est sensiblement plus lourde mais permettra d'aborder la phase d'optimisation suivante lus aisément.
            ;
            Case #PureBasic_Optimized_Code
              For x = 1 To DrawingWidth - 1
                For y = 1 To DrawingHeight - 1
                  xy = y * DrawingWidth + x
                  Value = ((DrawingArea(xy - DrawingWidth) + DrawingArea(xy + DrawingWidth) + DrawingArea(xy - 1) + DrawingArea(xy + 1)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArea(xy) = Color
                  Plot(x + xTop, y + yTop, Color)
                Next
              Next
            ;
            ; #PureBasic_LowLevel_Code uses both the array and a direct calculation of pixels addresses to the drawing buffer.
            ;
            ; This calculation will not make performances better right now, but will help to master direct copy of array values to pixels
            ;====================================================================================
            ; Dans #PureBasic_LowLevel_Code, on va utiliser simultanément un tableau pour mémoriser les points et un calcul de l'adresse des
            ; points dans le buffer d'écran.
            ;
            ; Ce calcul n'apportera pas directement d'améliorations de performances, mais permettra de maîtriser la copie directe d'une valeur du tableau sur l'écran.
            ;
            Case #PureBasic_LowLevel_Code
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              For x = 1 To DrawingWidth - 1
                X4 = DrawingBuffer + (x + xTop) << 2
                For y = 1 To DrawingHeight - 1
                  xy = y * DrawingWidth + x
                  Value = ((DrawingArea(xy - DrawingWidth) + DrawingArea(xy + DrawingWidth) + DrawingArea(xy - 1) + DrawingArea(xy + 1)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArea(xy) = Color
                  Color = (Color & $FF0000) >> 16 + (Color & $00FF00) + (Color & $0000FF) << 16
                  Address = DrawingBufferPitch * (y + yTop) + X4
                  PokeL(Address, Color)
                Next
              Next
            ;
            ; Here we try to "chew" the former code in order to get only simple low level instructions.
            ;
            ; Only one operation per line and no more than two variables in a single line.
            ;
            ; Thanks to PureBasic coding convention we can write :
            ;
            ; a + 1 instead of a = a + 1
            ; a + b instead of a = a + b
            ;
            ; etc
            ;
            ; c = a + b will be replaced by
            ; c = a
            ; c + b
            ;
            ; The resulting code will not be optimized as good as the compiler does, but it will be fast and close to machine level.
            ;===========================================================================
            ; Le but du code développé ici est de se rapprocher le plus possible des instructions les plus élémentaires.
            ;
            ; On essaye de proscrire toute écriture qui consiste à cumuler plusieurs opérations sur une même ligne
            ;
            ; Pour y parvenir il suffit d'ajouter des variables temporaires lorsque c'est nécessaire.
            ;
            ; Dans ce style d'écriture on utilisera de préférence la notation unaire de PureBasic, c'est à dire par exemple :
            ;
            ; a + 1 au lieu de a = a + 1
            ; a + b au lieu de a = a + b
            ;
            ; etc
            ;
            ; On s'interdit également d'utiliser trois variables sur une même ligne :
            ;
            ; c = a + b sera donc déployé en 2 lignes
            ; c = a
            ; c + b
            ;
            ; Cette écriture donnera un code très rapide, même si il n'est pas tout à fait aussi rapide que le modèle #PureBasic_LowLevel_Code précédent
            ;
            ; Cette étape est une manière réaliste de passer de l'optimisation langage évolué à l'optimisation assembleur. La tentative de traduire
            ; directement le code #PureBasic_Optimized_Code est généralement vouée à de malheureuses déconvenues et n'apporte jamais de meilleurs résultats
            ; que le code généré par le compilateur.
            ;
            Case #PureBasic_Chewed_Code
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              For x = 1 To DrawingWidth - 1
                X4 = x
                X4 + xTop
                X4 << 2
                X4 + DrawingBuffer
                For y = 1 To DrawingHeight - 1
                  xy = DrawingWidth
                  xy * y
                  xy + x
                  a = xy
                  a - DrawingWidth
                  Value = DrawingArea(a)
                  a = xy
                  a + DrawingWidth
                  Value + DrawingArea(a)
                  a = xy
                  a - 1
                  Value + DrawingArea(a)
                  a = xy
                  a + 1
                  Value + DrawingArea(a)
                  Value >> 2
                  Value + 1
                  Value & 63
                  Value + 192
                  Color = Value
                  Color << 10
                  Color + Value
                  DrawingArea(xy) = Color
                  Color1 = Color
                  Color1 & $FF0000
                  Color1 >> 16
                  Color2 = Color
                  Color2 & $00FF00
                  Color3 = Color
                  Color3 & $0000FF
                  Color3 << 16
                  Color = Color1
                  Color + Color2
                  Color + Color3
                  Address = y
                  Address + yTop
                  Address * DrawingBufferPitch
                  Address + X4
                  PokeL(Address, Color)
                Next
              Next
            ;
            ; Here we take the chewed code and translate it to ASM with some more optimization
            ;
            ; Especially we take care to avoid to store values in unnecessary variables.
            ;
            ; We can consider processor's registers as variables but with the limitation that any PureBasic instruction inserted between ASM lines
            ; may change registers values.
            ;
            ; So this is necessary to stay in ASM as far as possible to not loose registers values otherwise it is necessary to store registers all the time.
            ;=========================================================================================
            ; Le code #ASM_Code est la reprise du code #PureBasic_Chewed_Code avec quelques optimisations
            ;
            ; On s'attache en particulier à éliminer le report de valeurs de registres dans des variables temporaires.
            ;
            ; La limitation en nombre de registres oblige bien entendu à être très soigneux dans le choix des variables temporaires éliminées.
            ;
            ; On peut considérer pratiquement que les registres sont des variables, mais en conservant à l'esprit que l'exécution
            ; de toute instruction langage évolué remet en question l'état de tous les registres CPU.
            ;
            ; Il faut par conséquent rester en assembleur le plus longtemps possible pour éviter de risquer de perdre la valeur d'un registre en cours de route,
            ; ou inversement d'être contraint de sauvegarder tel ou tel registre dans une variable mémoire ce qui diminuerait la qualité d'optimisation.
            ;
            Case #ASM_Code
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              X4.l
              xy.l
              For x = 1 To DrawingWidth - 1
                !  MOV     eax, dword[v_x]                                     ; eax = x
                !  ADD     eax, dword[v_xTop]                               ; eax + xTop
                !  SAL      eax, 2                                                    ; eax << 2
                !  ADD     eax, dword[v_DrawingBuffer]                  ; eax + DrawingBuffer
                !  MOV     dword[v_X4], eax                                   ; X4 = eax
                For y = 1 To DrawingHeight - 1
                  !  MOV     eax, dword[v_DrawingWidth]                           ; eax = DrawingWidth
                  !  IMUL    eax, dword[v_y]                                    ; eax * y
                  !  ADD     eax, dword[v_x]                                   ; eax + x

                  !  MOV     ebp, dword[a_DrawingArea]                   ; ebp = @DrawingArea()
                  !  MOV     ecx, eax                                               ; ecx = eax
                  !  SUB      ecx, dword[v_DrawingWidth]                           ; ecx - DrawingWidth
                  !  SAL      ecx, 2                                                   ; ecx * 4
                  !  MOV     edi, dword[ebp+ecx]                              ; edi = PeekL(ebp + ecx)

                  !  MOV     ecx, eax                                                ; ecx = eax
                  !  ADD     ecx, dword[v_DrawingWidth]                            ; ecx + DrawingWidth
                  !  SAL      ecx, 2                                                   ; ecx * 4
                  !  ADD     edi, dword[ebp+ecx]                              ; edi + PeekL(ebp + ecx)

                  !  MOV     ecx, eax                                                ; ecx = eax
                  !  DEC     ecx                                                        ; ecx - 1
                  !  SAL      ecx, 2                                                    ; ecx * 4
                  !  ADD     edi, dword[ebp+ecx]                               ; edi + PeekL(ebp+ecx)

                  !  ADD     ecx, 8                                                    ; ecx + 8 (soit ecx = ((eax - 1) + 2) * 4
                  !  ADD     edi, dword[ebp+ecx]                               ; edi + PeekL(ebp + ecx)
                  
                  !  SAR     edi, 2                                                     ; edi >> 2
                  !  INC      edi                                                        ; edi + 1
                  !  And     edi, 63                                                   ; edi & 63
                  !  ADD     edi, 192                                                  ; edi + 192

                  !  MOV     edx, edi                                                 ; edx = edi
                  !  MOV     ebx, edx                                                ; ebx = edx
                  !  SAL      edx, 10                                                  ; eax = edx << 10
                  !  ADD     edx, ebx                                                ; edx = ebx

                  !  MOV      ecx, eax                                                ; ecx = eax
                  !  SAL       ecx, 2                                                   ; ecx * 4
                  !  MOV      [ebp+ecx], edx                                      ; PokeL(ebp+ecx, edx)

                  !  MOV      ecx, dword[v_y]                                  ; ecx = y
                  !  ADD      ecx, dword[v_yTop]                            ; ecx + yTop
                  !  IMUL     ecx, dword[v_DrawingBufferPitch]         ; ecx * DrawingBufferPitch
                  !  ADD      ecx, dword[v_X4]                                ; ecx + X4
                  !  BSWAP  edx                                                     ; edx (00RRGGBB) => (BBGGRR00)
                  !  SAR       edx, 8                                                 ; edx(BBGGRR00) => (00BBGGRR)
                  !  MOV      [ecx], edx                                           ; PokeL(ecx, edx)
                Next
              Next
            ;
            ; In #PureBasic_LowLevel_Code2 the same idea is used than in former #PureBasic_LowLevel_Code but addressing is linearized 
            ; Buffer addressing is no more x, y based but only a single variable is used.
            ;
            ; The address of the horizontal neighbor of a given point is the address of this point -/+ 1.
            ; The address of the vertical neighbor of a given point is the address of this point -/+ DrawingWidth.
            ;
            ; Using this method the point to draw is accessed the fastest.
            ;
            ; Meantime pixels values are stored in the array which is addressed using two variables to calculate unary index
            ; of the array easily, allowing to avoid the double address calculation.
            ;
            ; Obviously this does not look far from former code, it renders completely different results.
            ;
            ; In order to access to the best possible ASM optimization later, For / Next loops are replaced by Repeat / Until.
            ;
            ; Pixel value calculation is unchanged.
            ;=====================================================================
            ; Dans la version #PureBasic_LowLevel_Code2 on reprend le même principe mais on a linéarisé l'adressage
            ; Ici le buffer de l'écran est adressé avec une seule variable d'adresse qui est incrémentée de 4 octets pour passer
            ; point suivant et d'une ligne moins la DrawingWidth de tracé pour passer à la ligne suivante.
            ;
            ; Cette méthode permet d'adresser le pixel à tracer au plus vite.
            ;
            ; Dans le même temps les valeurs des points sont toujours stockées dans un tableau qui lui est adressé
            ; à partir de deux variables x et y en recalculant l'indice unaire du tableau pour éviter le double calcul d'adresse.
            ;
            ; La méthode paraît peu différente à priori, mais les résultats n'ont rien à voir.
            ;
            ; Pour amener à une meilleure optimisation ultérieure en assembleur, les boucles For / Next ont été remplacées par des Repeat / Until
            ;
            ; Le calcul de valeur d'un pixel reste inchangé.
            ;
            Case #PureBasic_LowLevel_Code2
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              BufferAddress = DrawingBuffer + 4 * (xTop + 1) + yTop * DrawingBufferPitch
              ArrayAddress = DrawingWidth + 1
              y = 1
              Repeat
                x = 1
                Repeat
                  Value = ((DrawingArea(ArrayAddress - DrawingWidth) + DrawingArea(ArrayAddress + DrawingWidth) + DrawingArea(ArrayAddress - 1) + DrawingArea(ArrayAddress + 1)) / 4 + 1) & 63 + 192
                  Color = Value << 10 + Value
                  DrawingArea(ArrayAddress) = Color
                  Color = (Color & $FF0000) >> 16 + (Color & $00FF00) + (Color & $0000FF) << 16
                  PokeL(BufferAddress, Color)
                  BufferAddress + 4
                  ArrayAddress + 1
                  x + 1
                Until  x > DrawingWidth - 1
                ArrayAddress + 1
                BufferAddress + DrawingBufferPitch - 4 * (DrawingWidth - 1)
                y + 1
              Until y > DrawingHeight - 1
            ;
            ; Here is the optimized ASM listing of the #PureBasic_LowLevel_Code2
            ;======================================================
            ; Voici une version assembleur optimisée pour le code #PureBasic_LowLevel_Code2
            ;
            Case #ASM_LowLevel_Code2
              DrawingBuffer = DrawingBuffer()
              DrawingBufferPitch = DrawingBufferPitch()
              !  MOV    ebx, dword[v_DrawingBuffer]                      ; ebx = DrawingBuffer
              !  MOV    edi, dword[v_xTop]                                     ; edi = xTop
              !  INC     edi                                                             ; edi + 1
              !  SAL     edi, 2                                                         ; edi * 4
              !  ADD    ebx, edi                                                      ; ebx + edi
              !  MOV    edi, dword[v_yTop]                                     ; edi = yTop
              !  IMUL   edi, dword[v_DrawingBufferPitch]                 ; edi * DrawingBufferPitch
              !  ADD    ebx, edi                                                     ; ebx + edi
              !  MOV    dword[v_BufferAddress], ebx                       ; BufferAddress = ebx
              !  MOV    ebx, dword[v_DrawingWidth]                       ; ebx = DrawingWidth
              !  INC     ebx                                                            ; ebx + 1
              !  MOV    dword[v_ArrayAddress], ebx                        ; ArrayAddress = ebx
              !  MOV    dword[v_y], 1                                             ; y = 1
              !_RepeatBN21:                                                         ; Repeat / Until return label
                !  MOV    dword[v_x], 1                                           ; x = 1
                !_RepeatBN22:                                                       ; Repeat / Until return label
                  !  MOV      ebp, dword[a_DrawingArea]                     ; ebp = DrawingArea
                  !  MOV      ecx, dword[v_ArrayAddress]                    ; ecx = @ArrayAddress
                  !  MOV      eax, ecx                                                 ; eax = ecx
                  !  SUB      eax, dword[v_DrawingWidth]                    ; eax - DrawingWidth
                  !  SAL       eax, 2                                                    ; eax * 4
                  !  MOV      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  MOV      eax, ecx                                                 ; eax = ecx
                  !  ADD      eax, dword[v_DrawingWidth]                    ; eax - DrawingWidth
                  !  SAL       eax, 2                                                    ; eax * 4
                  !  ADD      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  MOV      eax, ecx                                                 ; eax = ecx
                  !  DEC      eax                                                        ; eax - 1
                  !  SAL       eax, 2                                                    ; eax * 4
                  !  ADD      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  ADD      eax, 8                                                    ; eax + 8
                  !  ADD      edi, dword[ebp+eax]                               ; edi = PeekL(ebp+eax]

                  !  SAR      edi, 2                                                      ; edi / 4
                  !  INC       edi                                                         ; edi + 1
                  !  And      edi, 63                                                    ; edi & 63
                  !  ADD      edi, 192                                                  ; edi + 192
                  !  MOV      ebx, edi                                                  ; ebx = edi

                  !  SAL       ebx, 10                                                   ; ebx << 10
                  !  ADD      ebx, edi                                                  ; ebx + edi

                  !  MOV      eax, ecx                                                  ; eax = ecx
                  !  SAL       eax, 2                                                     ; eax * 4
                  !  MOV      dword[ebp+eax], ebx                               ; PokeL(ebp+eax, ebx)

                  !  BSWAP  ebx                                                         ; ebx (00RRGGBB) => (BBGGRR00)
                  !  SAR       ebx, 8                                                     ; ebx(BBGGRR00) => (00BBGGRR)

                  !  MOV      eax,dword[v_BufferAddress]                     ; eax = @BufferAddress
                  !  MOV      [eax], ebx                                               ; PokeL(eax, ebx)

                  !  ADD      dword[v_BufferAddress], 4                        ; BufferAddress + 4
                  !  INC       dword[v_ArrayAddress]                             ; ArrayAddress + 1
                  !  INC       dword[v_x]                                               ; x + 1
                  !  MOV      ebx, dword[v_x]                                        ; ebx = x
                  !  MOV      edi, dword[v_DrawingWidth]                       ; edi = DrawingWidth
                  !  DEC      edi                                                           ; edi - 1
                  !  CMP      ebx, edi                                                    ; if ebx <= edi
                  !  JLE        _RepeatBN22                                            ; Goto RepeatBN22
              
              !  INC     dword[v_ArrayAddress]                                   ; ArrayAddress + 1
              !  MOV    ebx, dword[v_DrawingWidth]                           ; ebx = DrawingWidth
              !  DEC    ebx                                                                ; ebx - 1
              !  SAL     ebx, 2                                                            ; ebx / 4
              !  NEG    ebx                                                                 ; -ebx
              !  ADD    ebx, dword[v_DrawingBufferPitch]                    ; ebx + DrawingBufferPitch
              !  ADD    dword[v_BufferAddress], ebx                           ; BufferAddress + ebx
              !  INC     dword[v_y]                                                     ; y + 1
              !  MOV    edi, dword[v_DrawingHeight]                            ; edi = DrawingHeight
              !  ADD    edi, -1                                                            ; edi - 1
              !  MOV    ebx, dword[v_y]                                              ; ebx = y
              !  CMP    ebx, edi                                                          ; if ebx <= edi
              !  JLE      _RepeatBN21                                                  ; Goto RepeatBN21
          EndSelect

          ;
          ; Keyboard events part
          ;============================
          ; Section de gestion des évènements clavier
          ;
          ExamineKeyboard()
          ; Change the code part to execute
          ; Modifie la section de code à exécuter
          If KeyboardPushed(#PB_Key_Left)
              ResetAll()
              ExecuteCode - 1
              If ExecuteCode < 0
                  ExecuteCode = 0
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Left)
          EndIf
          If KeyboardPushed(#PB_Key_Right)
              ResetAll()
              ExecuteCode + 1
              If ExecuteCode > #LastCode
                  ExecuteCode = #LastCode
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Right)
          EndIf
          ; Set / Unset the buffers synchronization
          ; Valide / invalide la synchronisation des buffers
          If KeyboardPushed(#PB_Key_Up)
              ResetAll()
              FlipBuffers = 1 - FlipBuffers
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Up)
          EndIf
          If KeyboardPushed(#PB_Key_Down)
              ResetAll()
              FlipBuffers = 1 - FlipBuffers
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Down)
          EndIf
          ; Change the drawing area size
          ; Modifie la taille de la zone de dessin
          If KeyboardPushed(#PB_Key_Pad4)
              ResetAll()
              WidthHeight - 1
              If WidthHeight < 0
                  WidthHeight = 0
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Pad4)
              DrawingWidth = DrawingWidth(WidthHeight)
              DrawingHeight = DrawingHeight(WidthHeight)
              xTop = (ScreenXSize - DrawingWidth) / 2 + 1
              yTop = (ScreenYSize - DrawingHeight) / 2 + 1
              xBottom = xTop + DrawingWidth - 2
              yBottom = yTop + DrawingHeight - 2
          EndIf
          If KeyboardPushed(#PB_Key_Pad6)
              ResetAll()
              WidthHeight + 1
              If WidthHeight > LastSize
                  WidthHeight = LastSize
              EndIf
              Repeat
                ExamineKeyboard()
              Until KeyboardReleased(#PB_Key_Pad6)
              DrawingWidth = DrawingWidth(WidthHeight)
              DrawingHeight = DrawingHeight(WidthHeight)
              xTop = (ScreenXSize - DrawingWidth) / 2 + 1
              yTop = (ScreenYSize - DrawingHeight) / 2 + 1
              xBottom = xTop + DrawingWidth - 2
              yBottom = yTop + DrawingHeight - 2
          EndIf
          ; Control the elapsed time since the last integer second
          ; Contrôle le temps écoulé depuis la dernière seconde entière
          If ElapsedMilliseconds() - tz => 1000
              tz = ElapsedMilliseconds()
              FPS = NFrames
              TFrames + FPS
              NFrames = 0
              AFrames + 1
          EndIf
          NFrames + 1
          ; Display statistics and parameters
          ; Affiche les statistiques et les paramètres
          BackColor(0, 0, 0)
          FrontColor(255, 255, 255)
          Locate(10, 40)
          DrawText("FPS " + Str(FPS) + "      Average : " + StrF(TFrames / AFrames, 2))
          Locate(10, 60)
          DrawText(CodeType(ExecuteCode) + "            ")
          Locate(10, 80)
          DrawText("FlipBuffers(" + Str(FlipBuffers) + ")")
          Locate(10, 100)
          DrawText(Str(DrawingWidth) + "x" + Str(DrawingHeight))
        StopDrawing()
      Until KeyboardReleased(#pb_key_escape)
  EndIf 
End

DataSection
  Data.s "#PureBasic_Regular_Code", "#PureBasic_Better_Code", "#PureBasic_Optimized_Code", "#PureBasic_LowLevel_Code"
  Data.s "#PureBasic_Chewed_Code", "#ASM_Code", "#PureBasic_LowLevel_Code2", "#ASM_LowLevel_Code2"
  Data.l 128, 128
  Data.l 256, 256
  Data.l 320, 240
  Data.l 480, 360
  Data.l 640, 480
  Data.l 800, 600
EndDataSection


Publié : jeu. 17/juin/2004 21:58
par filperj
Je n'arrive même pas à repérer l'algo de martin dans tout ça :oops:
Je me demandai si tu avais trouvé une astuce pour éviter de lire 4 fois chaque octet, ou autre optimisation?

Publié : jeu. 17/juin/2004 22:54
par filperj
Ah, j'ai trouvé: tu utilises des longs pour des variables à 6 bits significatifs, tricheur :twisted:

Publié : jeu. 17/juin/2004 23:11
par Backup
chez moi le code de Fweil va trop vite !!
ça pert de son charme je ne suis plus "envouté par l'effet !!"
y a plus l'effet hipnotique !!

je prefere la version de filperj !! :D
(c'est mon avis !!)