Aussi pratique que canvas.pixels[ ] mais jusqu'à 450 fois plus rapide (grâce à scanline)

Description

Pour balayer un Bitmap, nous utilisons Scanline qui est la méthode la plus rapide.
Cependant, lorsque nous avons besoin de connaître et/ou de changer les composantes RGB d'un Pixel précis (typiquement dessiner une courbe point par point), nous utilisons souvent 'Bitmap.Canvas.Pixels[X,Y]' qui est bien pratique malgré sa lenteur. C'est facile et ça marche sur tous les formats de Pixel et sur toutes les largeurs de Bitmap.

Ce que je vous propose aujourd'hui, c'est une méthode qui allie la facilité d'utilisation de 'Bitmap.Canvas.Pixels[X,Y]' à la rapidité de ScanLine : GetpPix().

LE BUT était de trouver un algorithme, le plus rapide possible, permettant d'atteindre un pixel donné afin de pouvoir consulter et modifier sa couleur. Ceci devant être possible sur un PixelFormat de pf24bit ou de pf32bit (les plus courants).
Pour la performance, le travail devait se faire en mémoire, mais la largeur du Bitmap (surtout pour pf24bit) ne devait pas poser de problèmes.
Il s'agissait donc de faire correspondre des coordonnées (X,Y) du bitmap avec un pointeur précis de la zone mémoire où est stocké le Bitmap.

LES DIFFICULTES étaient liées à la dispositions des pixels en mémoire. En effet, la mémoire Windows est une sorte de trame basée sur 4 octets.
Les Bitmaps sont stockés ligne par ligne et il peut arriver que la fin d'une ligne de pixels ne coïncide pas avec la trame de la mémoire. La conséquence est qu'on peut trouver des pointeurs inutiles en fin de ligne, qui rompent la belle progression arithmétique de ScanLine. De plus, les lignes du Bitmap peuvent être stockées de deux manières : de bas en haut ou de haut en bas.
Pour vous faire une idée plus visuelle de la façon dont sont stockés les pixels en mémoire, je vous conseille vivement de consulter cette page :

http://msdn.microsoft.com/en-us/library/dd407212(VS.85).aspx

Je ne parlerai pas des RGB, BGR, BGRA et ARGB, sans doute aussi inventés par le même agité du bocal qui a commis la structure Bitmap et qu'il trouvait sans doute encore trop simple... lol

LES SOLUTIONS existent. Il s'agit de déterminer la longueur d'une ligne en mémoire afin de pouvoir calculer arithmétiquement le pointeur correspondant à la ligne Y, en incrémentant le pointeur donné par ScanLine. Ensuite, il suffit de l'incrémenter de X fois la taille d'un pixel pour trouver l'adresse du pointeur correspondant à (X,Y).
Les deux tailles possibles d'un pixel (3 ou 4 bytes), ainsi que l'ordre de stockage des bytes RGBA seront facilement résolus en n'utilisant qu'un pRGBTriple, pointeur qui ne renvoie que les trois couleurs de base sous forme de 3 bytes (NB: GetpPix ne nécessite donc pas de transtypage pour travailler sur les valeurs RGB).
Il existe plusieurs façons d'obtenir le taille d'une ligne mémoire, mais la plus élégante (celle de Danny Thorpe, ingénieur Borland) est de simplement calculer Scanline[1] - Scanline[0] (en Integer, bien sûr). En effet, la valeur absolue nous donnera la taille d'une ligne mémoire (en bytes), mais de plus le signe nous permettra de travailler indifféremment sur un Top-Down ou un Bottom-Up DIB.

Je pense que les débutants en savent maintenant assez pour comprendre ce code qui ne présente aucune difficulté (bien que les pointeurs c'est... pointu).

Conclusion :


NB1 : En plus de Canvas.Pixels[], j'ai comparé GetpPix() aux méthodes suivantes avec toujours de bien meilleurs résultats pour mon algo :
- Windows.SetPixel
- Windows.SetPixelV
- Windows.GetDIBits & Windows.SetDIBits

NB2 : La méthode peut être adaptée à tous les autres PixelFormat.

NB3 : Contrairemant à Canvas.Pixels[], GetpPix() plante quand (X,Y) se trouve en dehors de la surface du Bitmap. C'est voulu. Je trouve qu'il est plus logique d'éventuellement vérifier cette condition avant l'appel de la fonction dans le cas où cela pourrait se produire (ce qui est loin d'être toujours le cas).

A ma connaissance, cet algo n'a jamais été publié (ce qui m'étonne un peu, mais bon !.. Je me trompe peut-être).

Je remercie Cashemire pour sa gracieuse prestation (J'ai pas pris le chat de Bacterius car il est déjà en niveaux de gris). :)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.