/* Fonction affichant une ligne antialiasée de (X0,Y0) à (X1,Y1), utilisant * une approche d'antialiasing publiée par Xiaolin Wu dans le numéro de Juillet * 1991 de Computer Graphics. * Nécessite que la palette soit configurée de telle manière quelle comporte * NumLevels intensité de la couleur d'affichage souhaitée, commençant avec la * couleur BaseColor (100% d'intensité) et suivie par (NumLevels-1) niveaux de * couleurs d'intensité éventuellement plus faible * avec la couleur (BaseColor+NumLevels-1) étant l'intensité 0% de la couleur * d'affichage désirée * (noir) * Ce code convient pour des résolutions d'écran dont les lignes ont * une longueur inférieures à 1K ; * pour des lignes plus longues, l'arithmétique sur 32 bits doit être * utilisé pour éviter les problèmes dus à l'imprécision de la virgule fixe. * Aucun clipping n'est réalisé avec DrawWuLine; il doit être réalisé à un * niveau supérieur ou dans la fonction DrawPixel. * * Testé avec Borland C++ 4.02 en modèle small par Jim Mischel 12/16/94. */ extern void DrawPixel(int, int, int); /* affichage de lignes antialiasées de Wu. * (X0,Y0),(X1,Y1) = ligne à dessiner * BaseColor = numéro de couleur de la première couleur du bloc utilisé pour * l'antialiasing, la version 100% d'intensité de la couleur d'affichage * NumLevels =taille du bloc de couleur, avec BaseColor+NumLevels-1 étant la * version d'intensité 0% de la couleur d'affichage * IntensityBits = log base 2 de NumLevels; le nombre de bits utilisé pour * décrire l'intensité de la couleur d'affichage. 2**IntensityBits==NumLevels */ void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels, unsigned int IntensityBits) { unsigned int IntensityShift, ErrorAdj, ErrorAcc; unsigned int ErrorAccTemp, Weighting, WeightingComplementMask; int DeltaX, DeltaY, Temp, XDir; /* S'assure que la ligne passe de haut en bas */ if (Y0 > Y1) { Temp = Y0; Y0 = Y1; Y1 = Temp; Temp = X0; X0 = X1; X1 = Temp; } /* Affiche le pixel initial, qui est toujours coupé par la ligne et donc n'a pas besoin de pondération */ DrawPixel(X0, Y0, BaseColor); if ((DeltaX = X1 - X0) >= 0) { XDir = 1; } else { XDir = -1; DeltaX = -DeltaX; /* make DeltaX positive */ } /* Cas particulier des lignes horizontale, verticale, et diagonale, qui ne demandent aucune pondération car elles traversent directement le centre de chaque pixel */ if ((DeltaY = Y1 - Y0) == 0) { /* Ligne horizontale */ while (DeltaX-- != 0) { X0 += XDir; DrawPixel(X0, Y0, BaseColor); } return; } if (DeltaX == 0) { /* Ligne verticale */ do { Y0++; DrawPixel(X0, Y0, BaseColor); } while (--DeltaY != 0); return; } if (DeltaX == DeltaY) { /* Ligne diagonale */ do { X0 += XDir; Y0++; DrawPixel(X0, Y0, BaseColor); } while (--DeltaY != 0); return; } /* La ligne n'est ni horizontale, ni diagonale, ni verticale */ ErrorAcc = 0; /* initialise l'accumulateur d'erreur de la ligne à 0 */ /* nombre de bits par lequel décaler ErrorAcc pour obtenir le niveau d'intensité */ IntensityShift = 16 - IntensityBits; /* Masque utilisé pour permuter tous les bits avec une pondération d'intensité, donnant le résultat (1 - pondération d'intensité) */ WeightingComplementMask = NumLevels - 1; /* Est-ce une ligne d'axe principal X ou Y ? */ if (DeltaY > DeltaX) { /* Ligne d'axe principal Y; calcule la part fractionnelle d'une virgule fixe sur 16 bits d'un pixel qui avance sur X chaque fois que Y avance d'1 pixel, en tronquant le résultat afin que nous ne dépassions pas l'extrémité le long de l'axe X */ ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY; /* Affiche tous les pixels autres que le premier et le dernier */ while (--DeltaY) { ErrorAccTemp = ErrorAcc; /* rappelle l'erreur accumulée courante */ ErrorAcc += ErrorAdj; /* calcule l'erreur pour le prochain pixel */ if (ErrorAcc <= ErrorAccTemp) { /* L'accumulateur d'erreur est retourné, avançons la coordonnée X */ X0 += XDir; } Y0++; /* Y principal, donc nous avançons toujours Y */ /* Les bits IntensityBits les plus significatifs de ErrorAcc nous donne la pondération d'intensité pour ce pixel, et le complément de pondération pour le pixel associé */ Weighting = ErrorAcc >> IntensityShift; DrawPixel(X0, Y0, BaseColor + Weighting); DrawPixel(X0 + XDir, Y0, BaseColor + (Weighting ^ WeightingComplementMask)); } /* Affiche le dernier pixel, qui est toujours coupé par la ligne n'a donc as besoin de pondération */ DrawPixel(X1, Y1, BaseColor); return; } /*Ligne d'axe principal X; calcule la part fractionnelle de la virgule fixe d'un pixel qui avance le long de Y chaque fois que X avance d'1 pixel, en tronquant le résultat pour éviter de dépasser l'extrémité le long de l'axe X */ ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX; /* Affiche tous les pixels autres que les premier et dernier */ while (--DeltaX) { ErrorAccTemp = ErrorAcc; /* rappelle l'erreur accumulée courante */ ErrorAcc += ErrorAdj; /* calcule l'erreur pour le prochain pixel */ if (ErrorAcc <= ErrorAccTemp) { /* L'accumulateur d'erreur est retourné, avançons la coordonnée Y */ Y0++; } X0 += XDir; /* X principal, aussi nous avançons toujours X */ /* Les bits IntensityBits les plus significatifs de ErrorAcc nous donnent la pondération d'intensité pour ce pixel, et le complément de weighting pour le pixel paired */ Weighting = ErrorAcc >> IntensityShift; DrawPixel(X0, Y0, BaseColor + Weighting); DrawPixel(X0, Y0 + 1, BaseColor + (Weighting ^ WeightingComplementMask)); } /* Affiche le dernier pixel, qui est toujours coupé par la ligne et n'a pas besoin de pondération */ DrawPixel(X1, Y1, BaseColor); }