Previous | Table of Contents | Next

LISTING 39.4 L39-4.ASM

    ; Scan converts an edge from (X1,Y1) to (X2,Y2), not including the
    ; point at (X2,Y2). If SkipFirst == 1, the point at (X1,Y1) isn’t
    ; drawn; if SkipFirst == 0, it is. For each scan line, the pixel
    ; closest to the scanned edge without being to the left of the scanned
    ; edge is chosen. Uses an all-integer approach for speed & precision.
    ; C near-callable as:
    ;    void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart,
    ;     int SkipFirst, struct HLine **EdgePointPtr);
    ; Edges must not go bottom to top; that is, Y1 must be <= Y2.
    ; Updates the pointer pointed to by EdgePointPtr to point to the next
    ;  free entry in the array of HLine structures.

    HLine   struc
    XStart          dw     ?           ;X coordinate of leftmost pixel in scan line
    XEnd            dw     ?           ;X coordinate of rightmost pixel in scan line
    HLine   ends

    Parms   struc
                    dw     2 dup(?)    ;return address & pushed BP
    X1              dw     ?           ;X start coord of edge
    Y1              dw     ?           ;Y start coord of edge
    X2              dw     ?           ;X end coord of edge
    Y2              dw     ?           ;Y end coord of edge
    SetXStart       dw     ?           ;1 to set the XStart field of each
                                       ; HLine struc, 0 to set XEnd
    SkipFirst       dw     ?           ;1 to skip scanning the first point
                                       ; of the edge, 0 to scan first point
    EdgePointPtr     dw    ?           ;pointer to a pointer to the array of
                                       ; HLine structures in which to store
                                       ; the scanned X coordinates
    Parms   ends

    ;Offsets from BP in stack frame of local variables.
    AdvanceAmt      equ     -2
    Height          equ     -4
    LOCAL_SIZE      equ      4         ;total size of local variables

            .model small
            .code
            public _ScanEdge
            align  2
    _ScanEdge   proc
            push  bp                   ;preserve caller’s stack frame
            mov   bp,sp                ;point to our stack frame
            sub   sp,LOCAL_SIZE        ;allocate space for local variables
            push  si                   ;preserve caller’s register variables
            push  di
            mov   di,[bp+EdgePointPtr]
            mov   di,[di]              ;point to the HLine array
            cmp   [bp+SetXStart],1     ;set the XStart field of each HLine
                                       ; struc?
            jz    HLinePtrSet          ;yes, DI points to the first XStart
            add   di,XEnd              ;no, point to the XEnd field of the
                                       ; first HLine struc
    HLinePtrSet:
            mov   bx,[bp+Y2]
            sub   bx,[bp+Y1]           ;edge height
            jle   ToScanEdgeExit       ;guard against 0-length & horz edges
            mov   [bp+Height],bx       ;Height = Y2 - Y1
            sub   cx,cx                ;assume ErrorTerm starts at 0 (true if
                                       ; we’re moving right as we draw)
            mov   dx,1                 ;assume AdvanceAmt = 1 (move right)
            mov   ax,[bp+X2]
            sub   ax,[bp+X1]           ;DeltaX = X2 - X1
            jz    IsVertical           ;it’s a vertical edge--special case it
            jns   SetAdvanceAmt        ;DeltaX >= 0
            mov   cx,1                 ;DeltaX < 0 (move left as we draw)
            sub   cx,bx                ;ErrorTerm = -Height + 1
            neg   dx                   ;AdvanceAmt = -1 (move left)
            neg   ax                   ;Width = abs(DeltaX)
    SetAdvanceAmt:
            mov   [bp+AdvanceAmt],dx
    ; Figure out whether the edge is diagonal, X-major (more horizontal),
    ; or Y-major (more vertical) and handle appropriately.
            cmp   ax,bx                 ;if Width==Height, it’s a diagonal edge
            jz    IsDiagonal            ;it’s a diagonal edge--special case
            jb    YMajor                ;it’s a Y-major (more vertical) edge
                                        ;it’s an X-major (more horz) edge
            sub   dx,dx                 ;prepare DX:AX (Width) for division
            div   bx                    ;Width/Height
                                        ;DX = error term advance per scan line
            mov   si,ax                 ;SI = minimum # of pixels to advance X
                                        ; on each scan line
            test  [bp+AdvanceAmt],8000h ;move left or right?
            jz    XMajorAdvanceAmtSet   ;right, already set
            neg   si                    ;left, negate the distance to advance
                                        ; on each scan line
    XMajorAdvanceAmtSet:                ;
            mov   ax,[bp+X1]            ;starting X coordinate
            cmp   [bp+SkipFirst],1      ;skip the first point?
            jz    XMajorSkipEntry       ;yes
    XMajorLoop:
            mov   [di],ax               ;store the current X value
            add   di,size HLine         ;point to the next HLine struc
    XMajorSkipEntry:
            add   ax,si                 ;set X for the next scan line
            add   cx,dx                 ;advance error term
            jle   XMajorNoAdvance       ;not time for X coord to advance one
                                        ; extra
            add   ax,[bp+AdvanceAmt]    ;advance X coord one extra
            sub   cx,[bp+Height]        ;adjust error term back
    XMajorNoAdvance:
            dec   bx                    ;count off this scan line
            jnz   XMajorLoop
            jmp   ScanEdgeDone
            align 2
    ToScanEdgeExit:
            jmp   ScanEdgeExit
            align2
    IsVertical:
            mov   ax,[bp+X1]            ;starting (and only) X coordinate
            sub   bx,[bp+SkipFirst]     ;loop count = Height - SkipFirst
            jz    ScanEdgeExit          ;no scan lines left after skipping 1st
    VerticalLoop:
            mov   [di],ax               ;store the current X value
            add   di,size HLine         ;point to the next HLine struc
            dec   bx                    ;count off this scan line
            jnz   VerticalLoop
            jmp   ScanEdgeDone
            align 2
    IsDiagonal:
            mov   ax,[bp+X1]            ;starting X coordinate
            cmp   [bp+SkipFirst],1      ;skip the first point?
            jz    DiagonalSkipEntry     ;yes
    DiagonalLoop:
            mov   [di],ax               ;store the current X value
            add   di,size HLine         ;point to the next HLine struc
    DiagonalSkipEntry:
            add   ax,dx                 ;advance the X coordinate
            dec   bx                    ;count off this scan line
            jnz   DiagonalLoop
            jmp   ScanEdgeDone
            align 2
    YMajor:
            pushbp                      ;preserve stack frame pointer
            mov   si,[bp+X1]            ;starting X coordinate
            cmp   [bp+SkipFirst],1      ;skip the first point?
            mov   bp,bx                 ;put Height in BP for error term calcs
            jz    YMajorSkipEntry       ;yes, skip the first point
    YMajorLoop:
            mov   [di],si               ;store the current X value
            add   di,size HLine         ;point to the next HLine struc
    YMajorSkipEntry:
            add   cx,ax                 ;advance the error term
            jle   YMajorNoAdvance       ;not time for X coord to advance
            add   si,dx                 ;advance the X coordinate
            sub   cx,bp                 ;adjust error term back
    YMajorNoAdvance:
            dec   bx                    ;count off this scan line
            jnz   YMajorLoop
            popbp                       ;restore stack frame pointer
    ScanEdgeDone:
            cmp   [bp+SetXStart],1       ;were we working with XStart field?
            jz    UpdateHLinePtr         ;yes, DI points to the next XStart
            sub   di,XEnd                ;no, point back to the XStart field
    UpdateHLinePtr:
            mov   bx,[bp+EdgePointPtr]   ;point to pointer to HLine array
            mov   [bx],di                ;update caller’s HLine array pointer
    ScanEdgeExit:
            pop   di                     ;restore caller’s register variables
            pop   si
            mov   sp,bp                  ;deallocate local variables
            pop   bp                     ;restore caller’s stack frame
            ret
    _ScanEdge   endp
            end

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash