/*Implémentation de tracé de ligne par traitements segmentés pour le mode 0x13, le mode 320x200 256 couleurs du VGA. Non optimisée! Compilé avec Borland C++ 4.02 en modèle small et lié avec L15-2.C Testé par Jim Mischel 11/30/94 */ #include #define SCREEN_WIDTH 320 #define SCREEN_SEGMENT 0xA000 void DrawHorizontalRun(char far **ScreenPtr, int XAdvance, int RunLength, int Color); void DrawVerticalRun(char far **ScreenPtr, int XAdvance, int RunLength, int Color); /* Affiche une ligne, dans la couleur Color, entre les extrémités spécifiées */ void LineDraw(int XStart, int YStart, int XEnd, int YEnd, int Color) { int Temp, AdjUp, AdjDown, ErrorTerm, XAdvance, XDelta, YDelta; int WholeStep, InitialPixelCount, FinalPixelCount, i, RunLength; char far *ScreenPtr; /* Nous afficherons toujours de haut en bas, pour réduire le nombre de cas à gérer, et pour que les lignes entre les mêmes extrémités affichent les mêmes pixels */ if (YStart > YEnd) { Temp = YStart; YStart = YEnd; YEnd = Temp; Temp = XStart; XStart = XEnd; XEnd = Temp; } /* Pointe sur l'adresse bitmap du premier pixel à afficher */ ScreenPtr = MK_FP(SCREEN_SEGMENT, YStart * SCREEN_WIDTH + XStart); /* Calcule si nous partons de la gauche ou de la droite et la distance horizontale nous parcourons */ if ((XDelta = XEnd - XStart) < 0) { XAdvance = -1; XDelta = -XDelta; } else { XAdvance = 1; } /* Calcule la distance verticale que nous parcourons */ YDelta = YEnd - YStart; /* Cas particuliers des lignes horizontale, verticale et diagonale, pour la vitesse et pour éviter des effets de bords et la division par 0 */ if (XDelta == 0) { /* Ligne verticale */ for (i=0; i<=YDelta; i++) { *ScreenPtr = Color; ScreenPtr += SCREEN_WIDTH; } return; } if (YDelta == 0) { /* Ligne horizontale */ for (i=0; i<=XDelta; i++) { *ScreenPtr = Color; ScreenPtr += XAdvance; } return; } if (XDelta == YDelta) { /* Ligne diagonale */ for (i=0; i<=XDelta; i++) { *ScreenPtr = Color; ScreenPtr += XAdvance + SCREEN_WIDTH; } return; } /* Détermine si la ligne a un axe principal X ou Y, traite en fonction */ if (XDelta >= YDelta) { /* Ligne axe principal X */ /* Nombre minimum de pixels pour traiter cette ligne */ WholeStep = XDelta / YDelta; /* Le terme d'erreur s'ajuste chaque fois que Y avance de 1; utilisé pour indiquer quand un pixel supplémentaire devrait être affiché, pour tenir compte des étapes fractionnaires le long de l'axe X pour 1 pixel le long de l'axe Y */ AdjUp = (XDelta % YDelta) * 2; /* Ajuste le terme d'erreur quand il déborde, utilisé pour effectuer l'avancée de X à ce moment là */ AdjDown = YDelta * 2; /* Terme d'erreur initial; correspond à l'avancée initiale de 0.5 le long de l'axe Y */ ErrorTerm = (XDelta % YDelta) - (YDelta * 2); /* Le premier et dernier traitement sont segmentés, car Y avance seulement de 0.5 et non de 1. Divise un traitement complet, plus le pixel initial, entre le premier et dernier traitement */ InitialPixelCount = (WholeStep / 2) + 1; FinalPixelCount = InitialPixelCount; /* Si la longueur du traitement de base est paire et qu'il n'y a pas d'avancée fractionnaire, nous avons un pixel qui pourrait aller soit avec le premier soit avec la dernier traitement segmenté, que nous allouerons de manière arbitraire au dernier. */ if ((AdjUp == 0) && ((WholeStep & 0x01) == 0)) { InitialPixelCount--; } /* Si le nombre de pixels par traitement est impair, nous avons 1 pixel qui ne peut être alloué ni au premier ni au dernier traitement segmenté, nous devons alors ajouter 0.5 au terme d'erreur, ce pixel sera donc traité par une boucle normale de traitement complet */ if ((WholeStep & 0x01) != 0) { ErrorTerm += YDelta; } /* Affiche le premier traitement segmenté des pixels */ DrawHorizontalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color); /* Affiche tous les traitements complets */ for (i=0; i<(YDelta-1); i++) { RunLength = WholeStep; /* le traitement est au moins de cette longueur*/ /* Avance le terme d'erreur et ajoute un pixel supplémentaire si le terme l'indique */ if ((ErrorTerm += AdjUp) > 0) { RunLength++; ErrorTerm -= AdjDown; /* réinitialise le terme d'erreur */ } /* Affiche le traitement de cette ligne d'affichage */ DrawHorizontalRun(&ScreenPtr, XAdvance, RunLength, Color); } /* Affiche le dernier traitement de pixels */ DrawHorizontalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color); return; } else { /* Ligne principale Y */ /* Nombre minimal de pixels dans le traitement de cette ligne */ WholeStep = YDelta / XDelta; /* Le terme d'erreur s'ajuste chaque fois que X avance de 1; utilisé pour indiquer quand un pixel supplémentaire devrait être affiché, pour tenir compte des étapes fractionnaires le long de l'axe Y pour 1 pixel le long de l'axe X */ AdjUp = (YDelta % XDelta) * 2; /* Ajuste le terme d'erreur quand il déborde, utilisé pour effectuer l'avancée de Y au même moment */ AdjDown = XDelta * 2; /* Terme d'erreur initial; reflète l'avancé initiale de 0.5 le long de l'axe X */ ErrorTerm = (YDelta % XDelta) - (XDelta * 2); /* Le premier et dernier traitement sont segmentés, car X avance seulement de 0.5 et non de 1. Divise un traitement complet, plus le pixel initial, entre le premier et dernier traitement */ InitialPixelCount = (WholeStep / 2) + 1; FinalPixelCount = InitialPixelCount; /* Si la longueur de l'exécution est paire et s'il n'y a pas d'avancée fractionnaire, nous avons 1 pixel qui pourrait aller soit avec le premier soit avec le dernier traitement segmenté, que nous allouerons de manière arbitraire à la dernière exécution */ if ((AdjUp == 0) && ((WholeStep & 0x01) == 0)) { InitialPixelCount--; } /* Si le nombre de pixels par traitement est impair, nous avons un pixel qui ne peut être alloué ni au premier, ni au dernier traitement segmenté, nous ajouterons alors 0.5 au terme d'erreur ,ce pixel sera donc traité par une boucle normale de traitement complet */ if ((WholeStep & 0x01) != 0) { ErrorTerm += XDelta; } /* Affiche le premier traitement segmenté des pixels */ DrawVerticalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color); /* Affiche toutes les traitements */ for (i=0; i<(XDelta-1); i++) { RunLength = WholeStep; /* le traitement est au moins de cette longueur */ /* Avance le terme d'erreur et ajoute un pixel supplémentaire si le terme d'erreur l'indique */ if ((ErrorTerm += AdjUp) > 0) { RunLength++; ErrorTerm -= AdjDown; /* reinitialise le terme d'erreur */ } /* Affiche le traitement de cette ligne d'affichage */ DrawVerticalRun(&ScreenPtr, XAdvance, RunLength, Color); } /* Affiche la dernier traitement de pixels */ DrawVerticalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color); return; } } /* Affiche un traitement horizontal de pixels, puis avance le pointeur bitmap sur le premier pixel du prochain traitement. */ void DrawHorizontalRun(char far **ScreenPtr, int XAdvance, int RunLength, int Color) { int i; char far *WorkingScreenPtr = *ScreenPtr; for (i=0; i