; Fonction appelable en C near pour tracer une ligne antialiasée de ; (X0,Y0) à(X1,Y1), en mode 13h, le mode standard 320x200 en 256 couleurs ; du VGA. Emploie 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 sorte que les niveaux d'intensité NumLevels ; de la couleur désirée commencent à la couleur BaseColor (100% d'intensité) ; suivie par les niveaux (NumLevels-1)d'intensité décroissante, ; avec la couleur(BaseColor+NumLevels-1) étant l'intensité 0% de la couleur ; d'affichage désirée (noir). Aucun clipping n'est réalisé dans DrawWuLine. ; Traite un maximum de 256 niveaux d'intensité par couleur antialiasée ; Ce code convient à pour des résolutions d'écran dont les lignes ; ne dépasse pas 1K de long; pour les lignes plus longues, ; l'arithmétique 32 bits doit être utilisé pour éviter les problèmes dus ; à l'imprécision des virgules fixes. ; Testé avec TASM 4.0 par Jim Mischel 12/16/94. ; ; Appelable en C near comme suit: ; void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, ; int NumLevels, unsigned int IntensityBits); SCREEN_WIDTH_IN_BYTES equ 320 ;nombre d'octets depuis le début d'une ligne ;d'affichage au début de la prochaine SCREEN_SEGMENT equ 0a000h ;segment dans lequel réside la mémoire de l'écran ; Paramètres passés dans la stack frame. parms struc dw 2 dup (?) ;BP empilé et adresse de retour X0 dw ? ;cordonnée X du point de départ de la ligne Y0 dw ? ;coordonnée Y du point de départ de la ligne X1 dw ? ;coordonnée X du point de fin de la ligne Y1 dw ? ;coordonnée Y du point de fin de la ligne BaseColor dw ? ;numéro de couleur de la première couleur dans le bloc ;utilisé pour l'antialiasing, la version d'intensité 100% ;de la couleur d'affichage NumLevels dw ? ;taille du bloc de couleur, avec BaseColor+NumLevels-1 ;étant la version d'intensité 0% de la couleur d'affichage ;(maximum NumLevels = 256) IntensityBits dw ? ;log base 2 de NumLevels ; nombre de bits utilisés pour ; décrire l'intensité de la couleur d'affichage. ; 2**IntensityBits==NumLevels ; (maximum IntensityBits = 8) parms ends .model small .code ;Dimension globale de l'écran, utilisée dans le programme principal _ScreenWidthInPixels dw 320 _ScreenHeightInPixels dw 200 .code public _DrawWuLine _DrawWuLine proc near push bp ;préserve la stack frame de l'appelant mov bp,sp ;pointe sur la stack frame locale push si ;préserve variables registre de C push di push ds ;préserve segment de données de C par défaut cld ;S'assure que la ligne passe de haut en bas. mov si,[bp].X0 mov ax,[bp].Y0 cmp ax,[bp].Y1 ;permute les extrémités si nécessaire pour vérifier que jna NoSwap ; Y0 <= Y1 xchg [bp].Y1,ax mov [bp].Y0,ax xchg [bp].X1,si mov [bp].X0,si NoSwap: ; Affiche le pixel initial, qui est toujours coupé par la ligne ; et n'a pas besoin de pondération. mov dx,SCREEN_SEGMENT mov ds,dx ;DS pointe sur le segment de l'écran mov dx,SCREEN_WIDTH_IN_BYTES mul dx ;Y0 * SCREEN_WIDTH_IN_BYTES rapporte l'offset ; de départ du début de la rangée dans laquelle ; est le pixel initial add si,ax ;DS:SI pointe sur le pixel initial mov al,byte ptr [bp].BaseColor ;couleur d'affichage mov [si],al ;affiche le pixel initial mov bx,1 ;XDir = 1; suppose que DeltaX >= 0 mov cx,[bp].X1 sub cx,[bp].X0 ;DeltaX; est-il >= 1? jns DeltaXSet ;oui, déplacement gauche->droite, ;non, déplacement droite->gauche neg cx ;rend DeltaX positif neg bx ;XDir = -1 DeltaXSet: ; Cas particulier des lignes horizontale, verticale, et diagonale, ; qui ne demandent aucune pondération car elles traversent directement le centre ; de chaque pixel. mov dx,[bp].Y1 sub dx,[bp].Y0 ;DeltaY; est-ce 0? jnz NotHorz ;non, pas horizontale ;oui, il est horizontal, cas particulier and bx,bx ;affiche gauche ->droite? jns DoHorz ;oui std ;non, affiche droite->gauche DoHorz: lea di,[bx+si] ;DI pointe sur le prochain pixel à afficher mov ax,ds mov es,ax ;ES:DI pointe sur le prochain pixel à afficher mov al,byte ptr [bp].BaseColor ;couleur d'affichage ;CX = DeltaX en ce point rep stosb ;trace le reste de la ligne horizontale cld ;restaure le drapeau de direction par défaut jmp Done ;et nous avons fini align 2 NotHorz: and cx,cx ; DeltaX est-il à 0? jnz NotVert ;non, ce n'est pas une ligne verticale ;oui, c'en est une, cas particulier mov al,byte ptr [bp].BaseColor ;couleur d'affichage VertLoop: add si,SCREEN_WIDTH_IN_BYTES ;pointe sur le prochain pixel à afficher mov [si],al ;affiche le prochain pixel dec dx ;--DeltaY jnz VertLoop jmp Done ;et nous avons fini align 2 NotVert: cmp cx,dx ;DeltaX == DeltaY? jnz NotDiag ;non, ce n'est pas une diagonale ;oui, c'en est une, cas particulier mov al,byte ptr [bp].BaseColor ;couleur d'affichage DiagLoop: lea si,[si+SCREEN_WIDTH_IN_BYTES+bx] ;passe au prochain pixel pour afficher ; en incrémentant Y et en ajoutant XDir à X mov [si],al ;affiche le prochain pixel dec dx ;--DeltaY jnz DiagLoop jmp Done ;et nous avons fini ; La ligne n'est ni horizontale, ni diagonale, ni verticale. align 2 NotDiag: ;Est-ce une ligne d'axe principal X ou Y ? cmp dx,cx jb XMajor ;axe principal X ; Ligne d'axe principal X. Calcule la part fractionnelle en 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 pour éviter tout dépassement de l'extrémité le long ;de l'axe X. xchg dx,cx ;DX = DeltaX, CX = DeltaY sub ax,ax ;DeltaX 16.16 devient une valeur en virgule fixe dans DX:AX div cx ;AX = (DeltaX << 16) / DeltaY. Pas de débordement ; car DeltaX < DeltaY mov di,cx ;DI = DeltaY (compteur de boucle) sub si,bx ;réduit le début de X de 1, comme expliqué ci-dessous mov dx,-1 ;initialise l'accumulateur d'erreur de la ligne à -1, ; si bien qu'il se retourne immédiatement et ; avance X à l'X de départ. Ce qui est nécessaire ; pour prévenir les sommes d'erreur vers 0 pour ; signifier "avancer la prochaine fois" et non "avancer ;cette fois," si bien que la somme d'erreur finale ne ; peut jamais tracer pour dépasser la coordonnée X ; finale mov cx,8 ;CL = nombre de bits par lesquels décaler sub cx,[bp].IntensityBits ; ErrorAcc pour obtenir le niveau ; d'intensité(8 au lieu de 16 car nous travaillons ; avec l'octet de poids fort de ErrorAcc) mov ch,byte ptr [bp].NumLevels ;masque utilisé pour permuter tous les bits dec ch ; dans weighting d'intensité, donnant ; le résultat (1 - weighting d'intensité) mov bp,BaseColor[bp] ;***stack frame n'est pas disponible*** ;***à partir de maintenant *** xchg bp,ax ;BP = ErrorAdj, AL = BaseColor, ; AH = registre scratch ; Affiche tous les pixels restant. YMajorLoop: add dx,bp ;calcule l'erreur du prochain pixel jnc NoXAdvance ;ce n'est pas encore le moment d'avancer dans X ;l'accumulateur d'erreur est retourné, ;avançons la coordonnée X add si,bx ;ajoute XDir au pointeur de pixel NoXAdvance: add si,SCREEN_WIDTH_IN_BYTES ;axe principal Y, avançons Y ; Les bits IntensityBits les plus significatifs de ErrorAcc nous donnent la ; pondération d'intensité pour ce pixel, et le complément de pondération pour le ; pixel associé. mov ah,dh ;msb of ErrorAcc shr ah,cl ;Weighting = ErrorAcc >> IntensityShift; add ah,al ;BaseColor + Weighting mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting); mov ah,dh ;msb of ErrorAcc shr ah,cl ;Weighting = ErrorAcc >> IntensityShift; xor ah,ch ;Weighting ^ WeightingComplementMask add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask) mov [si+bx],ah ;DrawPixel(X+XDir, Y, ; BaseColor + (Weighting ^ WeightingComplementMask)); dec di ;--DeltaY jnz YMajorLoop jmp Done ;nous avons fini cette ligne ;Ligne d'axe principal X. align 2 XMajor: ; Calcule la part fractionnaire en virgule fixe sur 16 bits d'un pixel qui ; avance Y chaque fois que X avance d'1 pixel, en tronquant le résultat pour ; éviter tout dépassement de l'extrémité le long de l'axe X. sub ax,ax ;DeltaY 16.16 devient une valeur en virgule fixe DX:AX div cx ;AX = (DeltaY << 16) / Deltax. Pas de débordement ; car DeltaY < DeltaX mov di,cx ;DI = DeltaX (compteur de boucle) sub si,SCREEN_WIDTH_IN_BYTES ;réduit le début de X de 1, ; comme expliqué ci-dessous mov dx,-1 ;initialise l'accumulateur d'erreur de la ligne à -1, ; si bien qu'il se retourne immédiatement et ; avance Y vers l'Y de départ. Ce qui nécessaire ; pour prévenir les sommes d'erreur de 0 pour dire ; "avancer la prochaine fois" et non "avancer ; cette fois mov cx,8 ;CL = nombre de bits par lesquels décaler sub cx,[bp].IntensityBits ; ErrorAcc pour obtenir le niveau ; d'intensité (8 au lieu de16 car nous travaillons ; avec l'octet de poids fort de ErrorAcc) mov ch,byte ptr [bp].NumLevels ;masque utilisé pour permuter les bits dec ch ; dans une weighting d'intensité, donnant ; résultat (1 - weighting d'intensité) mov bp,BaseColor[bp] ;***stack frame n'est pas disponible*** ;***à partir de maintenant *** xchg bp,ax ;BP = ErrorAdj, AL = BaseColor, ; AH = registre scratch ; Affiche tous les pixels restant. XMajorLoop: add dx,bp ;calcule l'erreur du prochain pixel jnc NoYAdvance ;ce n'est pas encore le moment d'avancer Y ;l'accumulateur d'erreur est retourné, ; avançons la coordonnée Y add si,SCREEN_WIDTH_IN_BYTES ;avance Y NoYAdvance: add si,bx ;axe principal X, ajoutons XDir au pointeur du pixel ; Les bits IntensityBits les plus significatifs de ErrorAcc nous donnent ; la pondération d'intensité pour ce pixel, et le complément de pondération ; d'intensité pour le pixel associé. mov ah,dh ;msb de ErrorAcc shr ah,cl ;Weighting = ErrorAcc >> IntensityShift; add ah,al ;BaseColor + Weighting mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting); mov ah,dh ;msb of ErrorAcc shr ah,cl ;Weighting = ErrorAcc >> IntensityShift; xor ah,ch ;Weighting ^ WeightingComplementMask add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask) mov [si+SCREEN_WIDTH_IN_BYTES],ah ;DrawPixel(X, Y+SCREEN_WIDTH_IN_BYTES, ; BaseColor + (Weighting ^ WeightingComplementMask)); dec di ;--DeltaX jnz XMajorLoop Done: ;nous avons fini cette ligne pop ds ;restaure le segment de données par défaut de C pop di ;restaure les variables registre de C pop si pop bp ;restaure la stack frame de l'appelant ret ;fini _DrawWuLine endp end