home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj9103.zip / GRAPHICS.ASC < prev    next >
Text File  |  1991-02-15  |  16KB  |  428 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. /* Draws all pixels in the list of horizontal lines passed in, in
  8.    mode 13h, the VGA's 320x200 256-color mode. Uses memset to fill
  9.    each line, which is much faster than using DrawPixel but requires
  10.    that a large data model (compact, large, or huge) be in use when
  11.    running in real mode or 286 protected mode.
  12.    All C code tested with Turbo C++ */
  13.  
  14. #include <string.h>
  15. #include <dos.h>
  16. #include "polygon.h"
  17.  
  18. #define SCREEN_WIDTH    320
  19. #define SCREEN_SEGMENT  0xA000
  20.  
  21. void DrawHorizontalLineList(struct HLineList * HLineListPtr,
  22.       int Color)
  23. {
  24.    struct HLine *HLinePtr;
  25.    int Length, Width;
  26.    unsigned char far *ScreenPtr;
  27.  
  28.    /* Point to the start of the first scan line on which to draw */
  29.    ScreenPtr = MK_FP(SCREEN_SEGMENT,
  30.          HLineListPtr->YStart * SCREEN_WIDTH);
  31.  
  32.    /* Point to the XStart/XEnd descriptor for the first (top)
  33.       horizontal line */
  34.    HLinePtr = HLineListPtr->HLinePtr;
  35.    /* Draw each horizontal line in turn, starting with the top one and
  36.       advancing one line each time */
  37.    Length = HLineListPtr->Length;
  38.    while (Length-- > 0) {
  39.       /* Draw the whole horizontal line if it has a positive width */
  40.       if ((Width = HLinePtr->XEnd - HLinePtr->XStart + 1) > 0)
  41.          memset(ScreenPtr + HLinePtr->XStart, Color, Width);
  42.       HLinePtr++;                /* point to next scan line X info */
  43.       ScreenPtr += SCREEN_WIDTH; /* point to next scan line start */
  44.    }
  45. }
  46.  
  47.  
  48. [LISTING TWO]
  49.  
  50. /* Scan converts an edge from (X1,Y1) to (X2,Y2), not including the
  51.    point at (X2,Y2). If SkipFirst == 1, the point at (X1,Y1) isn't
  52.    drawn; if SkipFirst == 0, it is. For each scan line, the pixel
  53.    closest to the scanned edge without being to the left of the
  54.    scanned edge is chosen. Uses an all-integer approach for speed and
  55.    precision */
  56.  
  57. #include <math.h>
  58. #include "polygon.h"
  59.  
  60. void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart,
  61.       int SkipFirst, struct HLine **EdgePointPtr)
  62. {
  63.    int Y, DeltaX, Height, Width, AdvanceAmt, ErrorTerm, i;
  64.    int ErrorTermAdvance, XMajorAdvanceAmt;
  65.    struct HLine *WorkingEdgePointPtr;
  66.  
  67.    WorkingEdgePointPtr = *EdgePointPtr; /* avoid double dereference */
  68.    AdvanceAmt = ((DeltaX = X2 - X1) > 0) ? 1 : -1;
  69.                             /* direction in which X moves (Y2 is
  70.                                always > Y1, so Y always counts up) */
  71.  
  72.    if ((Height = Y2 - Y1) <= 0)  /* Y length of the edge */
  73.       return;     /* guard against 0-length and horizontal edges */
  74.  
  75.    /* Figure out whether the edge is vertical, diagonal, X-major
  76.       (mostly horizontal), or Y-major (mostly vertical) and handle
  77.       appropriately */
  78.    if ((Width = abs(DeltaX)) == 0) {
  79.       /* The edge is vertical; special-case by just storing the same
  80.          X coordinate for every scan line */
  81.       /* Scan the edge for each scan line in turn */
  82.       for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
  83.          /* Store the X coordinate in the appropriate edge list */
  84.          if (SetXStart == 1)
  85.             WorkingEdgePointPtr->XStart = X1;
  86.          else
  87.             WorkingEdgePointPtr->XEnd = X1;
  88.       }
  89.    } else if (Width == Height) {
  90.       /* The edge is diagonal; special-case by advancing the X
  91.          coordinate 1 pixel for each scan line */
  92.       if (SkipFirst) /* skip the first point if so indicated */
  93.          X1 += AdvanceAmt; /* move 1 pixel to the left or right */
  94.       /* Scan the edge for each scan line in turn */
  95.       for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
  96.          /* Store the X coordinate in the appropriate edge list */
  97.          if (SetXStart == 1)
  98.             WorkingEdgePointPtr->XStart = X1;
  99.          else
  100.             WorkingEdgePointPtr->XEnd = X1;
  101.          X1 += AdvanceAmt; /* move 1 pixel to the left or right */
  102.       }
  103.    } else if (Height > Width) {
  104.       /* Edge is closer to vertical than horizontal (Y-major) */
  105.       if (DeltaX >= 0)
  106.          ErrorTerm = 0; /* initial error term going left->right */
  107.       else
  108.          ErrorTerm = -Height + 1;   /* going right->left */
  109.       if (SkipFirst) {   /* skip the first point if so indicated */
  110.          /* Determine whether it's time for the X coord to advance */
  111.          if ((ErrorTerm += Width) > 0) {
  112.             X1 += AdvanceAmt; /* move 1 pixel to the left or right */
  113.             ErrorTerm -= Height; /* advance ErrorTerm to next point */
  114.          }
  115.       }
  116.       /* Scan the edge for each scan line in turn */
  117.       for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
  118.          /* Store the X coordinate in the appropriate edge list */
  119.          if (SetXStart == 1)
  120.             WorkingEdgePointPtr->XStart = X1;
  121.          else
  122.             WorkingEdgePointPtr->XEnd = X1;
  123.          /* Determine whether it's time for the X coord to advance */
  124.          if ((ErrorTerm += Width) > 0) {
  125.             X1 += AdvanceAmt; /* move 1 pixel to the left or right */
  126.             ErrorTerm -= Height; /* advance ErrorTerm to correspond */
  127.          }
  128.       }
  129.    } else {
  130.       /* Edge is closer to horizontal than vertical (X-major) */
  131.       /* Minimum distance to advance X each time */
  132.       XMajorAdvanceAmt = (Width / Height) * AdvanceAmt;
  133.       /* Error term advance for deciding when to advance X 1 extra */
  134.       ErrorTermAdvance = Width % Height;
  135.       if (DeltaX >= 0)
  136.          ErrorTerm = 0; /* initial error term going left->right */
  137.       else
  138.          ErrorTerm = -Height + 1;   /* going right->left */
  139.       if (SkipFirst) {   /* skip the first point if so indicated */
  140.          X1 += XMajorAdvanceAmt;    /* move X minimum distance */
  141.          /* Determine whether it's time for X to advance one extra */
  142.          if ((ErrorTerm += ErrorTermAdvance) > 0) {
  143.             X1 += AdvanceAmt;       /* move X one more */
  144.             ErrorTerm -= Height; /* advance ErrorTerm to correspond */
  145.          }
  146.       }
  147.       /* Scan the edge for each scan line in turn */
  148.       for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
  149.          /* Store the X coordinate in the appropriate edge list */
  150.          if (SetXStart == 1)
  151.             WorkingEdgePointPtr->XStart = X1;
  152.          else
  153.             WorkingEdgePointPtr->XEnd = X1;
  154.          X1 += XMajorAdvanceAmt;    /* move X minimum distance */
  155.          /* Determine whether it's time for X to advance one extra */
  156.          if ((ErrorTerm += ErrorTermAdvance) > 0) {
  157.             X1 += AdvanceAmt;       /* move X one more */
  158.             ErrorTerm -= Height; /* advance ErrorTerm to correspond */
  159.          }
  160.       }
  161.    }
  162.  
  163.    *EdgePointPtr = WorkingEdgePointPtr;   /* advance caller's ptr */
  164. }
  165.  
  166.  
  167.  
  168. [LISTING THREE]
  169.  
  170. ; Draws all pixels in the list of horizontal lines passed in, in
  171. ; mode 13h, the VGA's 320x200 256-color mode. Uses REP STOS to fill
  172. ; each line.
  173. ; C near-callable as:
  174. ;     void DrawHorizontalLineList(struct HLineList * HLineListPtr,
  175. ;          int Color);
  176. ; All assembly code tested with TASM 2.0 and MASM 5.0
  177.  
  178. SCREEN_WIDTH    equ    320
  179. SCREEN_SEGMENT    equ    0a000h
  180.  
  181. HLine    struc
  182. XStart    dw    ?    ;X coordinate of leftmost pixel in line
  183. XEnd    dw    ?    ;X coordinate of rightmost pixel in line
  184. HLine    ends
  185.  
  186. HLineList struc
  187. Lngth    dw    ?    ;# of horizontal lines
  188. YStart    dw    ?    ;Y coordinate of topmost line
  189. HLinePtr dw    ?    ;pointer to list of horz lines
  190. HLineList ends
  191.  
  192. Parms    struc
  193.         dw    2 dup(?) ;return address & pushed BP
  194. HLineListPtr    dw    ?    ;pointer to HLineList structure
  195. Color        dw    ?    ;color with which to fill
  196. Parms    ends
  197.  
  198.     .model small
  199.     .code
  200.     public _DrawHorizontalLineList
  201.     align    2
  202. _DrawHorizontalLineList    proc
  203.     push    bp        ;preserve caller's stack frame
  204.     mov    bp,sp        ;point to our stack frame
  205.     push    si        ;preserve caller's register variables
  206.     push    di
  207.     cld            ;make string instructions inc pointers
  208.  
  209.     mov    ax,SCREEN_SEGMENT
  210.     mov    es,ax    ;point ES to display memory for REP STOS
  211.  
  212.     mov    si,[bp+HLineListPtr] ;point to the line list
  213.     mov    ax,SCREEN_WIDTH    ;point to the start of the first scan
  214.     mul    [si+YStart]    ; line in which to draw
  215.     mov    dx,ax        ;ES:DX points to first scan line to
  216.                 ; draw
  217.     mov    bx,[si+HLinePtr] ;point to the XStart/XEnd descriptor
  218.                 ; for the first (top) horizontal line
  219.     mov    si,[si+Lngth]    ;# of scan lines to draw
  220.     and    si,si        ;are there any lines to draw?
  221.     jz    FillDone    ;no, so we're done
  222.     mov    al,byte ptr [bp+Color] ;color with which to fill
  223.     mov    ah,al        ;duplicate color for STOSW
  224. FillLoop:
  225.     mov    di,[bx+XStart]    ;left edge of fill on this line
  226.     mov    cx,[bx+XEnd]    ;right edge of fill
  227.     sub    cx,di
  228.     js    LineFillDone    ;skip if negative width
  229.     inc    cx        ;width of fill on this line
  230.     add    di,dx        ;offset of left edge of fill
  231.     test    di,1        ;does fill start at an odd address?
  232.     jz    MainFill    ;no
  233.     stosb            ;yes, draw the odd leading byte to
  234.                 ; word-align the rest of the fill
  235.     dec    cx        ;count off the odd leading byte
  236.     jz    LineFillDone    ;done if that was the only byte
  237. MainFill:
  238.     shr    cx,1        ;# of words in fill
  239.     rep    stosw        ;fill as many words as possible
  240.     adc    cx,cx        ;1 if there's an odd trailing byte to
  241.                 ; do, 0 otherwise
  242.     rep    stosb        ;fill any odd trailing byte
  243. LineFillDone:
  244.     add    bx,size HLine    ;point to the next line descriptor
  245.     add    dx,SCREEN_WIDTH    ;point to the next scan line
  246.     dec    si        ;count off lines to fill
  247.     jnz    FillLoop
  248. FillDone:
  249.     pop    di        ;restore caller's register variables
  250.     pop    si
  251.     pop    bp        ;restore caller's stack frame
  252.     ret
  253. _DrawHorizontalLineList    endp
  254.     end
  255.  
  256.  
  257. [LISTING FOUR]
  258.  
  259. ; Scan converts an edge from (X1,Y1) to (X2,Y2), not including the
  260. ; point at (X2,Y2). If SkipFirst == 1, the point at (X1,Y1) isn't
  261. ; drawn; if SkipFirst == 0, it is. For each scan line, the pixel
  262. ; closest to the scanned edge without being to the left of the scanned
  263. ; edge is chosen. Uses an all-integer approach for speed & precision.
  264. ; C near-callable as:
  265. ;    void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart,
  266. ;      int SkipFirst, struct HLine **EdgePointPtr);
  267. ; Edges must not go bottom to top; that is, Y1 must be <= Y2.
  268. ; Updates the pointer pointed to by EdgePointPtr to point to the next
  269. ;  free entry in the array of HLine structures.
  270.  
  271. HLine    struc
  272. XStart    dw    ?    ;X coordinate of leftmost pixel in scan line
  273. XEnd    dw    ?    ;X coordinate of rightmost pixel in scan line
  274. HLine    ends
  275.  
  276. Parms    struc
  277.         dw    2 dup(?) ;return address & pushed BP
  278. X1        dw    ?    ;X start coord of edge
  279. Y1        dw    ?    ;Y start coord of edge
  280. X2        dw    ?    ;X end coord of edge
  281. Y2        dw    ?    ;Y end coord of edge
  282. SetXStart    dw    ?    ;1 to set the XStart field of each
  283.                 ; HLine struc, 0 to set XEnd
  284. SkipFirst    dw    ?    ;1 to skip scanning the first point
  285.                 ; of the edge, 0 to scan first point
  286. EdgePointPtr    dw    ?    ;pointer to a pointer to the array of
  287.                 ; HLine structures in which to store
  288.                 ; the scanned X coordinates
  289. Parms    ends
  290.  
  291. ;Offsets from BP in stack frame of local variables.
  292. AdvanceAmt      equ     -2
  293. Height          equ     -4
  294. LOCAL_SIZE      equ     4    ;total size of local variables
  295.  
  296.     .model small
  297.     .code
  298.     public _ScanEdge
  299.     align    2
  300. _ScanEdge    proc
  301.     push    bp        ;preserve caller's stack frame
  302.     mov    bp,sp        ;point to our stack frame
  303.         sub     sp,LOCAL_SIZE   ;allocate space for local variables
  304.     push    si        ;preserve caller's register variables
  305.     push    di
  306.     mov    di,[bp+EdgePointPtr]
  307.     mov    di,[di]        ;point to the HLine array
  308.     cmp    [bp+SetXStart],1 ;set the XStart field of each HLine
  309.                 ; struc?
  310.     jz    HLinePtrSet    ;yes, DI points to the first XStart
  311.     add    di,XEnd        ;no, point to the XEnd field of the
  312.                 ; first HLine struc
  313. HLinePtrSet:
  314.     mov    bx,[bp+Y2]
  315.     sub    bx,[bp+Y1]    ;edge height
  316.     jle    ToScanEdgeExit    ;guard against 0-length & horz edges
  317.     mov    [bp+Height],bx    ;Height = Y2 - Y1
  318.     sub    cx,cx        ;assume ErrorTerm starts at 0 (true if
  319.                                 ; we're moving right as we draw)
  320.     mov    dx,1        ;assume AdvanceAmt = 1 (move right)
  321.     mov    ax,[bp+X2]
  322.     sub    ax,[bp+X1]      ;DeltaX = X2 - X1
  323.         jz      IsVertical         ;it's a vertical edge--special case it
  324.     jns    SetAdvanceAmt    ;DeltaX >= 0
  325.     mov    cx,1        ;DeltaX < 0 (move left as we draw)
  326.     sub    cx,bx        ;ErrorTerm = -Height + 1
  327.     neg    dx        ;AdvanceAmt = -1 (move left)
  328.         neg     ax              ;Width = abs(DeltaX)
  329. SetAdvanceAmt:
  330.     mov    [bp+AdvanceAmt],dx
  331. ; Figure out whether the edge is diagonal, X-major (more horizontal),
  332. ; or Y-major (more vertical) and handle appropriately.
  333.     cmp    ax,bx        ;if Width==Height, it's a diagonal edge
  334.     jz    IsDiagonal    ;it's a diagonal edge--special case
  335.     jb    YMajor        ;it's a Y-major (more vertical) edge
  336.                 ;it's an X-major (more horz) edge
  337.         sub     dx,dx           ;prepare DX:AX (Width) for division
  338.         div     bx        ;Width/Height
  339.                 ;DX = error term advance per scan line
  340.     mov    si,ax        ;SI = minimum # of pixels to advance X
  341.                 ; on each scan line
  342.         test    [bp+AdvanceAmt],8000h ;move left or right?
  343.         jz      XMajorAdvanceAmtSet ;right, already set
  344.         neg     si              ;left, negate the distance to advance
  345.                 ; on each scan line
  346. XMajorAdvanceAmtSet:            ;
  347.     mov    ax,[bp+X1]    ;starting X coordinate
  348.         cmp     [bp+SkipFirst],1 ;skip the first point?
  349.         jz    XMajorSkipEntry    ;yes
  350. XMajorLoop:
  351.     mov    [di],ax        ;store the current X value
  352.     add    di,size HLine    ;point to the next HLine struc
  353. XMajorSkipEntry:
  354.     add    ax,si        ;set X for the next scan line
  355.     add    cx,dx        ;advance error term
  356.     jle    XMajorNoAdvance ;not time for X coord to advance one
  357.                 ; extra
  358.     add    ax,[bp+AdvanceAmt] ;advance X coord one extra
  359.         sub     cx,[bp+Height]  ;adjust error term back
  360. XMajorNoAdvance:
  361.         dec     bx        ;count off this scan line
  362.         jnz     XMajorLoop
  363.     jmp    ScanEdgeDone
  364.     align    2
  365. ToScanEdgeExit:
  366.     jmp    ScanEdgeExit
  367.         align    2
  368. IsVertical:
  369.     mov    ax,[bp+X1]    ;starting (and only) X coordinate
  370.     sub    bx,[bp+SkipFirst] ;loop count = Height - SkipFirst
  371.         jz      ScanEdgeExit     ;no scan lines left after skipping 1st
  372. VerticalLoop:
  373.     mov    [di],ax        ;store the current X value
  374.     add    di,size HLine    ;point to the next HLine struc
  375.     dec    bx        ;count off this scan line
  376.     jnz    VerticalLoop
  377.     jmp    ScanEdgeDone
  378.         align    2
  379. IsDiagonal:
  380.     mov    ax,[bp+X1]    ;starting X coordinate
  381.         cmp     [bp+SkipFirst],1 ;skip the first point?
  382.     jz    DiagonalSkipEntry ;yes
  383. DiagonalLoop:    
  384.     mov    [di],ax        ;store the current X value
  385.     add    di,size HLine    ;point to the next HLine struc
  386. DiagonalSkipEntry:
  387.     add    ax,dx        ;advance the X coordinate
  388.     dec    bx        ;count off this scan line
  389.     jnz    DiagonalLoop
  390.     jmp    ScanEdgeDone
  391.         align    2
  392. YMajor:
  393.     push    bp        ;preserve stack frame pointer
  394.     mov    si,[bp+X1]    ;starting X coordinate
  395.         cmp     [bp+SkipFirst],1 ;skip the first point?
  396.     mov    bp,bx        ;put Height in BP for error term calcs
  397.         jz    YMajorSkipEntry    ;yes, skip the first point
  398. YMajorLoop:
  399.     mov    [di],si        ;store the current X value
  400.     add    di,size HLine    ;point to the next HLine struc
  401. YMajorSkipEntry:
  402.     add    cx,ax        ;advance the error term
  403.     jle    YMajorNoAdvance ;not time for X coord to advance
  404.     add    si,dx        ;advance the X coordinate
  405.         sub     cx,bp        ;adjust error term back
  406. YMajorNoAdvance:
  407.         dec     bx        ;count off this scan line
  408.         jnz     YMajorLoop
  409.     pop    bp        ;restore stack frame pointer
  410. ScanEdgeDone:
  411.     cmp    [bp+SetXStart],1 ;were we working with XStart field?
  412.     jz    UpdateHLinePtr    ;yes, DI points to the next XStart
  413.     sub    di,XEnd        ;no, point back to the XStart field
  414. UpdateHLinePtr:
  415.     mov    bx,[bp+EdgePointPtr] ;point to pointer to HLine array
  416.     mov    [bx],di        ;update caller's HLine array pointer
  417. ScanEdgeExit:
  418.     pop    di        ;restore caller's register variables
  419.     pop    si
  420.         mov     sp,bp           ;deallocate local variables
  421.     pop    bp        ;restore caller's stack frame
  422.     ret
  423. _ScanEdge    endp
  424.     end
  425.  
  426.  
  427.  
  428.