;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME =  POLYLINE.ASM
;*
;* DESCRIPTIVE NAME =  Polyline drawing device driver. 
;*
;*
;* VERSION      V2.0
;*
;* DATE         03/03/87
;*
;* DESCRIPTION  
;*              
;*   Given a set of points, draw a set of polylines connecting adjoining
;*   points.  Depending on whether we are writing to the EGA or a bitmap
;*   we initialize as necessary.  Solid and styled lines are handled.
;*   Small (<= 64k bytes) monochrome and color bitmaps and huge monochrome
;*   and color bitmaps are all supported.  A run length slice algorithm is
;*   used to determine the pixels used to draw each line.  The algorithm is
;*   explained later on.
;*  
;*   The line drawing code is slightly different depending on whether
;*   we are drawing solid lines to the EGA, solid lines to a bitmap, or
;*   styled lines to either the EGA or a memory bitmap.  For the sake
;*   of speed, a different set of line drawing routines is called
;*   for each case.
;*  
;*   There are sixteen raster operations (sets of logical operations) performed
;*   on the data written out.  When writing to the EGA there are four of
;*   these operations which take two passes of EGA memory.  In each of these
;*   cases the first passes sets the necessary bits in the necessary planes
;*   to ones or zeros explicitly.  The second pass then XORs the necessary
;*   bit in the necessary planes.  The other twelve raster operations can
;*   be done in one pass of EGA memory.  All raster operations are done in
;*   one pass of memory for bitmaps.  Depending on the raster operation and
;*   the color of the pen, it is easily determined whether we set bits to
;*   zeros, set bits to ones, invert bits or do nothing.  Bitmaps are written
;*   to one plane at a time.  In the case of a mono bitmap, only one plane is
;*   written to.
;*  
;*   Huge bitmaps are stored on a scan line basis.  For example, if we have
;*   three color planes, the the bitmap is stored as a red scan line, a green
;*   scan line, a blue scan line, a red scan line, and so on.  Because of this
;*   format, there will most likely be some left over bytes at the end of the
;*   segment.  These will have to be accounted for when crossing segment
;*   boundaries.
;*  
;*   Lines are drawn from left to right.  So if a line moves from right
;*   to left, the endpoints are swapped and the line is drawn from left to
;*   right.
;*  -----------------------------------------------------------------------;
;*  -----------------------------------------------------------------------;
;*                     Our Favorite Line Drawing Algorithm
;*  
;*   I derive here the Run Length Algorithm, originally due to Bresenham.
;*   (NATO ASI Series, Volume F17, Fundamental Algorithms for Computer Graphics,
;*   Springer-Verlag, Berlin Heidelberg 1985)
;*  
;*   Suppose we want to draw a line from integer points (0,0) to (dx,dy), and we
;*   are given that:
;*  
;*       0 < dx < 65536  and  0 <= dy <= dx/2.                               (1)
;*  
;*   It happens that it is useful to have dx/dy >= 2. This allows a faster
;*   algorithm and prevents overflow in some of the calculations which will
;*   involve 2 dy.
;*  
;*   Lines in other directions can be handled by flipping them in various
;*   directions to get them into the "lower half octant" of (1).  We will always
;*   assume that lines will be drawn left to right.  This gets rid of several
;*   cases of inner loop code.  Simply exchanging the end points of the line can
;*   insure left-to-rightness.  If |slope| > 1, the line is "y-major", and we will
;*   exchange the x and y coordinates to make it x-major.  If we then have
;*   slope < 0, we negate the y coordinate.  And finally, if slope > 1/2, we
;*   subtract the y coordinates from the x coordinates. This has the effect of
;*   reflecting the coordinates through the line of slope 1/2.  I will call this
;*   a "half flip". Note that the diagonal gets mapped into the x axis after a
;*   half flip.
;*  
;*   We must record each step of the transformation that we have done so that we
;*   will be able to do the rendering in the proper direction after calculating
;*   what pattern of pixels should be drawn.  We will also need to pay special
;*   attention to lines that have undergone a flip in the vertical direction,
;*   either through the x axis or the half flip.  I will call these lines
;*   "flipped".  If the line has been reflected by both of these, I will count it
;*   as not flipped.
;*  
;*   The first thing to decide is what scheme we will use to determine if a given
;*   pixel should be turned on, i.e. our "metric".  Our scheme is as follows:
;*   First, imagine that the real geometric line has been drawn from (0,0) to
;*   (dx,dy).  Second, draw a vertical line segment of length one centered on the
;*   pixel in question.  If the geometric line intersects the vertical line
;*   through our pixel, we will turn the pixel on, otherwise it stays off.  If the
;*   geometric line exactly hits the upper end of our vertical line we define this
;*   to be an intersection, but not if it exactly hits the bottom.  I.e. 1/2 gets
;*   rounded down.
;*  
;*   We have rotated and flipped some lines to get them under the restrictions of
;*   (1).  For consistency, we should use the same "metric" described above to
;*   decide what pixels should be on for any line BEFORE it is flipped around
;*   for our calculation.  Having a consistent metric this way will let our lines
;*   blend nicely with our curves. For lines that are "y-major", i.e. have
;*   |slope| > 1, the consistent metric would use horizontal lines through each
;*   pixel, and decide ties by rounding to the left.  If any line has undergone a
;*   vertical flip to fit (1), we'll need to know that, since the metric would
;*   then require that 1/2 be rounded down.
;*  
;*   I assume that all the pixels are arranged on a Cartesian coordinate system,
;*   and that the pixels live at the points where both x and y are integers.  I
;*   will call the lines of constant integer y coordinate "scans" or "scanlines".
;*   The line that we draw under restriction (1) will consist of varying numbers
;*   of pixels on successive scans, for example:
;*  
;*                        *****
;*                  ******
;*             *****
;*       ******
;*  
;*   Due to our choice of metric, we will never have one pixel turned on directly
;*   above another that's turned on.  We will also never have a gap, i.e. there
;*   will be exactly one pixel turned on for each integer x between 0 and dx. All
;*   that remains is to decide how many pixels should be turned on for each scan.
;*   Note that if we have done a half flip on our original line then the scans
;*   pictured above will actually run along a diagonal.
;*  
;*   Consider an arbitrary scan corresponding to the integer y.  Suppose that our
;*   geometric line intersects the "half scan" below this line, i.e. the
;*   horizontal line at y-1/2, at some rational number x.  The first pixel that
;*   should be turned on for this scan is given by:
;*  
;*       x     = floor(x) + 1                                            (2)
;*        first
;*  
;*   The floor()+1 function is important here.  If the geometric line intersects
;*   the half scan between two integers, the upper integer is the first pixel we
;*   should turn on.  If the geometric line intersects the half scan exactly at an
;*   integer, then we are at the very end of one of our vertical test lines and
;*   according to our metric the pixel in the scan below is the one to light.
;*   Therefore the next integer up is again the first pixel.  This is just
;*   floor(x)+1.
;*  
;*   Suppose we define the inverse slope to be:
;*  
;*       s = dx/dy,                                                      (3)
;*  
;*   then our geometric line will intersect the next half scan at x+s.  The last
;*   pixel we should light on this scan is therefore:
;*  
;*       x    = floor(x + s)                                             (4)
;*        last
;*  
;*   If the geometric line intersects the half scan between two integers, the
;*   lower one is the last pixel. If the geometric line intersects the half scan
;*   exactly at some integer, this is a tie case which our metric decides to put
;*   in this scan.  This is the floor function.
;*  
;*   The number of pixels to light on this scan is therefore:
;*  
;*       c  = x    - x     + 1 = floor(x + s) - floor(x),                (5)
;*        y    last   first
;*  
;*   The integer part of x passes out of the floor function and cancels out. If we
;*   define the fractional part:
;*  
;*       f = frac(x),                                                    (6)
;*  
;*   we then have:
;*  
;*       c  = floor(f + s) - floor(f)
;*        y
;*                                                                       (7)
;*          = floor(f + s)
;*  
;*   Since we know that the intersection of the geometric line with the half scan
;*   below y is at:
;*  
;*       x = (y - 1/2) s,                                                (8)
;*  
;*   we know that the f in (7) is given by:
;*  
;*       f = frac(ys - s/2).                                             (9)
;*  
;*   To handle the cases of flipped lines, we could just recalculate (2) and (4)
;*   with the consideration that the metric causes us to round up instead of down.
;*   This works, but leads to different formulas for the flipped and non-flipped
;*   cases, which would add complexity to our implementation. Instead we use a
;*   simplifying trick.  The business of whether the metric rounds up or down only
;*   concerns us in those cases where the geometric line exactly hits end of one
;*   of our vertical test lines.  This happens when the intersection of the
;*   geometric line with a half scan occurs exactly at an integer.  Suppose now
;*   that, if the line is a flipped one, we don't use the geometric line, but
;*   instead use the geometric line shifted left by 1/(2 dy). The shifted
;*   geometric line would simply intersect the test line in the scan above.  This
;*   is the result we would want from rounding up.  Now suppose that the
;*   intersection of the original geometric line with the halfscan is not exactly
;*   an integer.  Then it must be some number of 1/(2 dy) units above an integer.
;*   The shifted line will have an intersection 1/(2 dy) to the left. At worst,
;*   this would now be at the integer.  But if we use the usual metric to just
;*   round down, this will not change what pixel gets displayed.
;*  
;*   Thus, we can calculate everything about a round-up metric by using the usual
;*   round down metric with a shifted geometric line.  Equation (7) remains valid
;*   except that we need to redefine f from equation (9):
;*  
;*       f = frac(ys - s/2 + f ),                                        (10)
;*                            0
;*  
;*   where:
;*            { -1/(2 dy)  if the line is flipped
;*       f  = {                                                          (11)
;*        0   {     0      otherwise
;*  
;*   The behavior of c in equation (7), as a function of f, is very easy to
;*   characterize.  When f is smaller than 1-frac(s), it cannot push the
;*   argument of the floor function above the next integer.  The result is
;*   therefore floor(s).  As soon as f equals or exceeds 1-frac(s), the result
;*   becomes floor(s)+1.  If we define:
;*  
;*       m = floor(s),                                                   (12)
;*  
;*   we can say:
;*  
;*            {  m   if  f < 1 - frac(s)
;*       c  = {                                                          (13)
;*        y   { m+1  if  f >= 1 - frac(s)
;*  
;*   As an implementation aid, we will want to define:
;*  
;*       f' = f + frac(s) - 1.                                           (14)
;*  
;*   So that (13) becomes:
;*  
;*            {  m   if  f' < 0
;*       c  = {                                                          (15)
;*        y   { m+1  if  f' >= 0
;*  
;*   The first and last scans of our line will not, in general, have this many
;*   pixels since the geometric line terminates at the end points and doesn't
;*   therefore penetrate the whole scan.  Relation (15) tells us about all the
;*   scans in between the first and last scans.  We don't really want to
;*   calculate (10) for every scan, instead we can calculate it for the second
;*   scan and just increment it to get the rest.
;*  
;*       f  = frac(s/2 + f )
;*        1               0
;*                                                                       (16)
;*       f   = frac(s + f )
;*        N+1            N
;*  
;*   Our f' can be advanced for each scan nearly as easily as f, so instead we
;*   could use:
;*  
;*       f' = frac(s/2 + f ) + frac(s) - 1
;*        1               0
;*  
;*       f'  = frac((N+1)s - s/2 + f ) + frac(s) - 1
;*        N+1                       0
;*  
;*           = frac(s + Ns - s/2 + f ) + frac(s) - 1
;*                                  0
;*                                                                       (17)
;*           = frac(s + frac(Ns - s/2 + f )) + frac(s) - 1
;*                                       0
;*  
;*           = frac(s + f' - frac(s) + 1) + frac(s) - 1
;*                       N
;*  
;*           = frac(f') + frac(s) - 1
;*                   N
;*  
;*   And now we will calculate how many pixels to display on the first and last
;*   scans. We are given that the first pixel is at (0,0). The last pixel on the
;*   first scan is at:
;*  
;*       x    = floor(f + s/2)                                           (18)
;*        last         0
;*  
;*   The number of pixels on the first scan is therefore:
;*  
;*       c  = floor(f + s/2) + 1                                         (19)
;*        0          0
;*  
;*   As in (2), the first pixel on the last scan is at:
;*  
;*       x     = floor(dx + f - s/2) + 1                                 (20)
;*        first              0
;*  
;*   The number of pixels on the last scan is therefore:
;*  
;*       c  = dx - (floor(dx + f - s/2) + 1) + 1
;*        dy                    0
;*  
;*          = -floor(f - s/2)                                            (21)
;*                    0
;*  
;*          = ceil(s/2 - f )
;*                        0
;*  
;*   This        much wraps up the mathematical problem, if we are interested in
;*   drawing the whole line.  But in many cases we will want to clip the line.  I
;*   will assume that some clipping calculations have occurred before our line
;*   drawing routine is called.  I will assume that our routine is called with an
;*   interval, which is a subset of [0,dx], in which the line is to be drawn.  In
;*   order for external routines to be able to calculate this interval they will
;*   need to know our metric.
;*  
;*   Assume that the inclusive interval we are given is:
;*  
;*       [x , x ]  where  0 <= x <= x <= dx.                             (22)
;*         a   b                a    b
;*  
;*   Then the first and last scans we will draw on will be:
;*  
;*       y  = ceil((x - f )/s + 1/2) - 1
;*        a          a   0
;*                                                                       (23)
;*       y  = ceil((x - f )/s + 1/2) - 1
;*        b          b   0
;*  
;*   The number of pixels to draw on the first and last scans are:
;*  
;*       c  = floor(s(y + 1/2) + f ) - x + 1
;*        a            a          0     a
;*                                                                       (24)
;*       c  = x  - floor(s(y - 1/2) + f )
;*        b    b            b          0
;*  
;*   The fractional part of the intersection with the second scan is:
;*  
;*       f  = frac(s(y + 1) + f )                                        (25)
;*        1           a        0
;*  
;*   so that:
;*  
;*       f' = frac(s(y + 1/2) + f ) + frac(s) - 1                        (26)
;*        1           a          0
;*  
;*   The common subexpression in (24) and (26) is very convenient and will save us
;*   some computation.  Surprisingly, the argument to the ceil function in (23) is
;*   also intimately related to the subexpression.  Whereas it might look like it
;*   takes two multiplies and two divides to calculate (23), (24), and (26) for
;*   the first clipped scan, we can actually get by with only one multiply and two
;*   divides.  Here's how we do it.  Note that the argument to the ceil function
;*   of (23) is always a multiple of 1/(2 dx).  In much the same way that we have
;*   handled flipped lines, we can subtract 1/(2 dx) from the argument to turn the
;*   ceil function into a floor function.  Thus:
;*  
;*       y  = ceil((x - f )/s + 1/2) - 1
;*        a          a   0
;*  
;*          = floor((x - f )/s + 1/2 - 1/(2 dx))                         (27)
;*                    a   0
;*  
;*          = floor((2 dy x  - 2 dy f  + dx - 1) / (2 dx))
;*                         a         0
;*  
;*   When we do the division here, we will get back an integer quotient which
;*   is the y value we are looking for.  But we also get a remainder rm, which
;*   will be somewhere between 0 and 2 dx.  Putting the division back together
;*   we get:
;*  
;*       2 dx y  + rm = 2 dy x  - 2 dy f  + dx - 1                       (28)
;*             a              a         0
;*  
;*   Let's now go back to the subexpression we are interested in:
;*  
;*       s(y + 1/2) + f  = (2 dx y  + dx + 2 dy f ) / (2 dy)
;*          a          0          a              0
;*  
;*             = (2 dy x  + 2 dx - (rm + 1)) / (2 dy)
;*                      a                                                (29)
;*  
;*                    2 dx - (rm + 1)            (rm + 1)
;*             = x  + --------------- = x  + s - --------
;*                a        2 dy          a         2 dy
;*  
;*   Remember that rm is positive, but strictly less than 2 dx.  What this tells
;*   us is that the intersection of the geometric line with the next half scan
;*   must be above, but less than s above, the x point we are clipping to.  This
;*   makes perfect sense.  Thus, after computing our starting y scan, it takes
;*   only the one division in the final expression (29) to compute:
;*  
;*       c  = floor(s(y + 1/2) + f ) - x + 1
;*        a            a          0     a
;*                                                                       (30)
;*                       rm + 1
;*          = floor( s - ------ ) + 1,
;*                        2 dy
;*  
;*   and:
;*  
;*       f' = frac(s(y + 1/2) + f ) + frac(s) - 1
;*        1           a          0
;*                                                                       (31)
;*                      rm + 1
;*          = frac( s - ------ ) + frac(s) - 1.
;*                       2 dy
;*  
;*   Of course, an identical relationship exists for the ending scan, so that if
;*   we have the remainder rm' we can compute:
;*  
;*       c  = x  - floor(s(y - 1/2) + f )
;*        b    b            b          0
;*                                                                       (32)
;*                      rm'+ 1           rm'+ 1
;*          = - floor(- ------ ) = ceil( ------ )
;*                       2 dy             2 dy
;*  
;*   And we again save a multiply.  Of course, I anticipate that the vast majority
;*   of the time we will not clip, or will only clip a pixel off of one end.  We
;*   will have special case code to handle those times when we are only removing a
;*   few pixels from the first or last scan.
;*  
;*   We will implement all the above using a fractional representation, i.e. all
;*   rational numbers will be kept as multiples of 1/(2 dy).  This minimizes the
;*   computation involved.  I have not made this explicit since it obscures many
;*   otherwise very simple equations and derivations.
;*              
;*              
;*              
;* FUNCTIONS    do_polylines
;*                          
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   02/23/87                     Kent Settle [kentse] Major re-write.
;*   03/03/87                     Written by Kent Settle
;*   04/08/87                     Kent Settle [kentse] Modified to draw all
;*   04/08/87                     lines moving right.
;*   04/30/87                     Kent Settle [kentse] Added huge bitmap handling.
;*   10/23/87                     Wesley O. Rupel [wesleyr] Optimized, Added
;*   10/23/87                     FireWalls, removed pd_screen_sel
;*   05/07/88                     Charles Whitmer [chuckwh] Rewrote from scratch.
;*   05/07/88                     Moved half of the clipping work out of
;*   05/07/88                     polyline_workhorse_routine.
;*   05/07/88                     Charles Whitmer [chuckwh] Stole the guts of
;*   05/07/88                     polyline_set_ega_for_one_pass.
;*   05/07/88                     Charles Whitmer [chuckwh] New variables for the
;*   05/07/88                     new PolyLine scheme.
;*   05/07/88                     Charles Whitmer [chuckwh] Stole the guts of
;*   05/07/88                     polyline_set_ega_for_two_pass.
;*   05/08/88                     Charles Whitmer [chuckwh] Rewrote from scratch.
;*   05/08/88                     Clipping is vastly simplified.
;*   05/20/88                     Charles Whitmer [chuckwh] Derived all the
;*   05/20/88                     above for the case of clipped lines, which
;*   05/20/88                     Bresenham neglected to cover in his paper.
;*   06/20/88                     Bob Grudem [bobgru] Implemented the "max"
;*   06/20/88                     metric for line styling.
;*   06/18/89                     Lee A. Newberg [leen] Supports DisjointLines.
;*   04/09/90                     David Cook [davidcoo] Copied from corresponding
;*   04/09/90                     foreground routine.
;*   05/25/91            PSM04063 Paul R. King [PAULKING] Added the fComFlags
;*   05/25/91                     parameter to fix PTR #PSM04063
;*   05/25/91                     R. King [PAULKING] Added the fComFlags
;*   05/25/91                     parameter to fix PTR #PSM04063
;*
;*****************************************************************************/

        .xlist
        include cmacros.inc
INCL_GRE_LINES          equ                      1
INCL_DDIPATHS           equ                      1
INCL_GPIPRIMITIVES      equ                      1
        include pmgre.inc
DINCL_ROPS  equ     1
        include driver.inc
        include display.inc
        include egamemf.inc
        include egafam.inc
        include polyline.inc
        include njmp.mac
        .list

        public  style_table
        public  get_bm_info

        externA         DOSHUGEINCR
        externA         SCREEN_CBSCAN
        externA         SCREEN_CY

        externFP        DosGetSeg
        externFP        far_clip_line              ; CLIP.ASM

sBegin  Data
        externB         sdScreen
sEnd    Data


sBegin  Code
        assumes cs,Code
        externW     CodeData

        externB  Code_rop_flags                       ; Special EGA drawing mode flags
        externB  Code_rop_data_r                      ; EGA Data Rotate Register value
        externB  Code_rop_pen_and                     ; Special EGA Drawing mode AND mask
        externB  Code_rop_pen_xor                     ; Special EGA Drawing mode XOR mask

        externNP        dev_solid_horizontal      ; PLYSOLID.ASM
        externNP        dev_solid_vertical        ; PLYSOLID.ASM
        externNP        dev_solid_diagonal        ; PLYSOLID.ASM
        externNP        dev_styled_horizontal     ; POLYSTYL.ASM
        externNP        dev_styled_vertical       ; POLYSTYL.ASM
        externNP        dev_styled_diagonal       ; POLYSTYL.ASM
        externNP        bitmap_solid_horizontal  ; POLYBITM.ASM
        externNP        bitmap_solid_vertical     ; POLYBITM.ASM
        externNP        bitmap_solid_diagonal     ; POLYBITM.ASM
        externNP        bitmap_styled_horizontal ; POLYBITM.ASM
        externNP        bitmap_styled_vertical    ; POLYBITM.ASM
        externNP        bitmap_styled_diagonal    ; POLYBITM.ASM


;/*
;** the various line rendering routines
;*/

bitmap_styled_table     equ                      this word
        dw      bitmap_styled_horizontal
        dw      bitmap_styled_vertical
        dw      bitmap_styled_diagonal
        dw      bitmap_styled_diagonal

bitmap_solid_table      equ                      this word
        dw      bitmap_solid_horizontal
        dw      bitmap_solid_vertical
        dw      bitmap_solid_diagonal
        dw      bitmap_solid_diagonal

dev_solid_table         equ                      this word
        dw      dev_solid_horizontal
        dw      dev_solid_vertical
        dw      dev_solid_diagonal
        dw      dev_solid_diagonal

dev_styled_table        equ                      this word
        dw      dev_styled_horizontal
        dw      dev_styled_vertical
        dw      dev_styled_diagonal
        dw      dev_styled_diagonal

;/*
;** The bm_and_xor_table contains and-masks and xor-masks for
;** setting pels to one of four possible values.   The and-masks
;** have been inverted, and are stored in the low byte of each word.
;** The xor-masks are stored in the high bytes.
;**
;**       ROP     XOR    ~AND
;**       -------------------
;**       DDx     00      FF                        set to zero
;**       Dn      FF      00                        not dest
;**       D       00      00                        leave alone
;**       DDxn    FF      FF                        set to one
;*/

bm_and_xor_table        equ                      this word
        dw      000FFh                            ; set to zero
        dw      0FF00h                            ; not dest
        dw      00000h                            ; leave alone
        dw      0FFFFh                            ; set to one

;/*
;** The style_table contains style masks and their reverses, used for the
;** different line styles while drawing styled lines.
;*/

style_table     equ     this word
        db      10101010b, 01010101b             ; dotted line
        db      11001100b, 00110011b             ; short dash
        db      11100100b, 00100111b             ; dash dot
        db      10100000b, 00000101b             ; double dot
        db      11100111b, 11100111b             ; long dash
        db      11101010b, 01010111b             ; dash dot dot
        db      11111111b, 11111111b             ; solid
        db      00000000b, 00000000b             ; invisible
        db      10101010b, 01010101b             ; alternate dotted line

;/***************************************************************************
;*
;* FUNCTION NAME = do_polylines   
;*
;* DESCRIPTION   = Called by PolyLine.  Loops through all the line segments  
;*                 on the stack, computing the clipping required by          
;*                 polyline_workhorse_routine.  It then calculates all the   
;*                 DDA parameters and loops through the DDA.                 
;*                                                                           
;*                 Registers Destroyed:                                                                 
;*                       BX,CX,DX,SI,DI,DS                                                              
;*                 Registers Preserved:                                                                 
;*                       ES                                                                             
;*
;* INPUT         = DS:DI = DDC
;*                 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = new style position
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

;/*
;**!!! NOTE: the save of di & si is REQUIRED for parameter alignment
;*/



cProc   far_do_polylines,<PUBLIC,FAR,NODATA>,<es,di,si>
        parmW   fsPolyFlags
        parmW   cLines                            ;# of points
        parmW   npaptl                            ;--> to a set of points
        parmW   usFunN                            ;FunN.lo of calling Gre function
        parmW   fComFlags                         ;COM_FLAGS  (i.e. FunN.hi of calling Gre function
        include plylocal.inc                      

cBegin
        ddc?    di
        cld

        call    polyline_init

        assumes ds,nothing              ; DS => surface

setup_polyline:

        mov     es,CodeData
        assumes es,Data
        mov     ax,fsPolyFlags
        mov     Flags,ax
        mov     ax,cLines
        mov     Count,ax
        mov     ax,npaptl
        mov     Points,ax
        mov     npddc,di

;/*
;** copy information from the DDC to the stack        ;
;*/

        cmp     usFunN,off_PolyLine
        jnz     setup_disjointlines

;/*
;** draw first pel?
;*/

        mov     offNextLine,SIZE POINTL
        test    es:[di].ddc_fb,DDC_FIRST_PEL
        jz      setup_done
        or      Flags,PF_DRAW_FIRST_PEL
        jmp     short setup_done

setup_disjointlines:
        add     Points,SIZE POINTL                ; Ignore current position
        mov     offNextLine,2 * SIZE POINTL
        shr     Count,1                           ; Count = cPoints / 2

setup_done:
        and     es:[di].ddc_fb,not DDC_FIRST_PEL

;/*
;** the clip rectangle
;*/

        test    Flags,PF_BETTER_CLIP
        jz      no_clip_rect
        les     bx,es:[di].ddc_prddc                 ; ES:BX = RDDC
        assumes es,nothing
        rddc?   es,bx
        mov     ax,es:[bx].rddc_arcsClip.rcs_xLeft
        mov     lsg.lsg_rcsClip.rcs_xLeft,ax
        mov     ax,es:[bx].rddc_arcsClip.rcs_yBottom
        mov     lsg.lsg_rcsClip.rcs_yBottom,ax
        mov     ax,es:[bx].rddc_arcsClip.rcs_xRight
        mov     lsg.lsg_rcsClip.rcs_xRight,ax
        mov     ax,es:[bx].rddc_arcsClip.rcs_yTop
        mov     lsg.lsg_rcsClip.rcs_yTop,ax
        fw_zero <es>
no_clip_rect:

;/*
;** Loop through the lines                              ;
;*/

walk_thru_polylines:
        mov     lsg.lsg_fsLineFlags,0

;/*
;** load the endpoints into registers
;*/

        mov     si,Points
        mov     bx,offNextLine                     ; offset (bytes) between start of two lines
        add     Points,bx
        mov     bx,ss:[si].ptl_x.lo                ; (BX,CX) = point 1
        mov     cx,ss:[si].ptl_y.lo
        mov     di,ss:[si][SIZE POINTL].ptl_x.lo   ; (DI,SI) = point 2
        mov     si,ss:[si][SIZE POINTL].ptl_y.lo

;/*
;** handle non-clipped lines                            
;*/

        test    Flags,PF_BETTER_CLIP
        jnz     clipped_line

;/*
;** make sure we go left to right
;*/

        cmp     bx,di
        jle     is_left_to_right
        xchg    bx,di
        xchg    cx,si
        or      lsg.lsg_fsLineFlags,LF_LEFT_XCHG
is_left_to_right:

;/*
;** record the start position (so we can calculate the surface address)
;*/

        mov     lsg.lsg_xStart,bx
        mov     lsg.lsg_yStart,cx

;/*
;** normalize coordinates relative to xStart and yStart
;*/

        sub     di,bx
        sub     si,cx
        jge     y_be_unsigned
        neg     si
        or      lsg.lsg_fsLineFlags,LF_Y_FLIP + LF_VERTICAL_FLIP
y_be_unsigned:

;/*
;** get x-major
;*/

        cmp     si,di
        jbe     we_be_x_major
        xchg    si,di
        or      lsg.lsg_fsLineFlags,LF_XY_XCHG
we_be_x_major:

;/*
;** save the clip range
;*/

        mov     lsg.lsg_xA,0
        mov     lsg.lsg_xB,di
        jmp     short region_clipping_done

;/*
;** handle clipped lines                                
;*/

;/*
;** load y clipping, normalize y coordinates relative to yStart
;*/

clipped_line:
        push    bp           
        lea     bp,lsg                            ; point to LINESEGMENT structure
        mov     dx,-1                             ; no POINTFX clipping!
        call    far_clip_line
        pop     bp
region_clipping_done:

;/*
;** clip off the first pel, if needed                  
;*/

        cmp     usFunN,off_PolyLine
        jnz     draw_it_only_once                 ; DisjointLines always draws first pel
        test    Flags,PF_DRAW_FIRST_PEL
        jnz     draw_it_only_once
        test    lsg.lsg_fsLineFlags,LF_LEFT_XCHG
        jnz     is_left_exchanged
        cmp     lsg.lsg_xA,1
        jae     all_clipping_done
        mov     lsg.lsg_xA,1
        jmp     short all_clipping_done
is_left_exchanged:
        cmp     lsg.lsg_xB,di
        jb      all_clipping_done
        dec     di
        mov     lsg.lsg_xB,di
        inc     di
        jnz     all_clipping_done                ; watch out for the zero length line!

;/*
;** Stick in the totally clipped case.  We have to let this fall through
;** the style advancing code!
;*/

draw_no_pels:
        mov     lsg.lsg_xB,0
        mov     lsg.lsg_xA,1
        jmp     short all_clipping_done

;/*
;** if we draw the first pel this time, don't let it happen next time
;*/

draw_it_only_once:
        and     Flags,not PF_DRAW_FIRST_PEL
all_clipping_done:

;/*
;** compute the line style at both ends                
;*/

        cmp     LineStyle,LINETYPE_SOLID - 1
        njz     ignore_styles

;/*
;**----------start of max metric changes (bobgru)-----------------
;*/

        cmp     LineStyle,LINETYPE_ALTERNATE - 1
        jnz     determine_major_styling

        mov     ax,wStyleStepHorz
        mov     cx,ax                            ;wStyleStepVert = wStyleStepHorz
        mov     wStyleStepDiag,ax                ;wStyleStepDiag = "
        jmp     short store_style_steps


;/*
;** Determine whether line is styled as x or y major.
;** If LF_XY_XCHG is true, then DI will be delta_y, but
;** we want it to be delta_x, so swap SI and DI.
;*/

determine_major_styling:
        mov     ax,wStyleStepHorz
        mov     wStyleStepHorzTmp,ax
        mov     bx,wStyleStepVert
        mov     wStyleStepVertTmp,bx

        test    lsg.lsg_fsLineFlags,LF_XY_XCHG
        jz      @F
        xchg    si,di                             ; SI = delta_y, DI = delta_x
@@:
        mul     di                                ; DX:AX = x step * delta_x
        xchg    ax,bx                             ; 
        mov     cx,dx                             ; save all 32 bits
        mul     si                                ; DX:AX = y step * delta_y
        cmp     dx,cx
        jb      styled_as_x_major
        ja      styled_as_y_major
        cmp     ax,bx
        ja      styled_as_y_major

styled_as_x_major:
        mov     wStyleStepVertTmp,0
        jmp     short set_diag_style_step
styled_as_y_major:
        mov     wStyleStepHorzTmp,0
set_diag_style_step:
        mov     ax,wStyleStepHorzTmp
        mov     bx,wStyleStepVertTmp
        mov     cx,ax
        add     cx,bx

;/*
;** Determine major and side style steps.
;** If LF_XY_XCHG is true, then SI and DI will have been
;** swapped, so swap them back.
;*/

        test    lsg.lsg_fsLineFlags,LF_XY_XCHG
        jz      store_style_steps
        xchg    ax,bx                             ; AX = major step
        xchg    si,di                             ; DI = major, SI = minor
store_style_steps:
        mov     wStyleStepRun,ax
        mov     wStyleStepDiag,ax
        mov     wStyleStepSide,cx

;/*
;**----------end of max metric changes (bobgru)-----------------
;*/

;/*
;** compute the change in style
;*/

        mov     bx,di                             ; change = RunStep * (major - minor)
        sub     bx,si                             ;  + SideStep * minor
        mul     bx
        xchg    ax,cx
        mul     si
        add     ax,cx                             ; AX = style change
        mov     bx,wStyleCounter                  ; BX = style at start
        add     ax,bx                             ; AX = style at finish
        cmp     usFunN,off_PolyLine
        jnz     style_updated                     ; Reset style for DisjointLines
        mov     wStyleCounter,ax                  ; style at start of next line
style_updated:

;/*
;** reverse the style if we've exchanged left for right
;*/

        mov     dx,wStyleMask
        test    lsg.lsg_fsLineFlags,LF_LEFT_XCHG
        jz      @F
        mov     bx,ax
        not     bx                                ; BX = style count to use
        mov     dl,dh                             ; DL = style mask to use
@@:     mov     bActiveStyleCounter,bl
        mov     cl,bh
        and     cl,7
        rol     dl,cl
        mov     bActiveStyleMask,dl

;/*
;** do the styling version of a half-flip
;*/

        mov     ax,si
        add     ax,ax
        jc      flip_it_style                     ; 2dy > dx -- half flip needed
        cmp     ax,di
        jbe     @F                                ; 2dy <= dx -- no half flip needed
flip_it_style:
        sub     si,di
        neg     si
        xor     lsg.lsg_fsLineFlags,LF_HALF_FLIP + LF_VERTICAL_FLIP

;/*
;** exchange the style steps
;*/

        mov     ax,wStyleStepRun
        xchg    ax,wStyleStepSide
        mov     wStyleStepRun,ax
        mov     wStyleStepDiag,ax
@@:
        jmp     short half_flips_done

;/*
;** do half flip for the non-styled case               ;
;*/

ignore_styles:
        mov     ax,si
        add     ax,ax
        jc      flip_it                           ; 2dy > dx -- half flip needed
        cmp     ax,di
        jbe     half_flips_done                  ; 2dy <= dx -- no half flip needed
flip_it:
        sub     si,di
        neg     si
        xor     lsg.lsg_fsLineFlags,LF_HALF_FLIP + LF_VERTICAL_FLIP
half_flips_done:
        mov     lsg.lsg_cdx,di
        mov     lsg.lsg_cdy,si

;/*
;** call the workhorse to compute the rendering    
;*/

        mov     ax,lsg.lsg_xA                      ; get rid of clipped lines
        cmp     ax,lsg.lsg_xB
        ja      skip_this_line
        push    ds
        mov     ds,CodeData
        assumes ds,Data
        push    di
        mov     di,npddc
        mov     ax,fComFlags
        and     al,DDC_VISIBLE
        and     al,[di].ddc_fb
        assumes ds,nothing
        pop     di
        pop     ds
        je      skip_this_line

        call    polyline_workhorse_routine
skip_this_line:
        dec     Count
        njnz    walk_thru_polylines

;/*
;** restore any hardware defaults
;*/

        call    npfnUnSetup

;/*
;** If the line is not solid and the background mix mode is not
;** transparent, then go back and draw the background line, unless we
;** have already.
;*/

        test    fsPolyFlags,PF_DOING_BACKGROUND
        jnz     done_background
        cmp     LineStyle,LINETYPE_SOLID - 1
        jz      done_background
        mov     ds,CodeData
        assumes ds,Data
        mov     di,npddc
        cmp     [di].ddc_la.la_ba.ba_bkmix,ROP_D    
        je      done_background
        call    polyline_background_init
        assumes ds,nothing
        or      fsPolyFlags,PF_DOING_BACKGROUND
        jmp     setup_polyline

done_background:

;/*
;** return the new style position
;*/
 
        mov     ax,wStyleCounter
cEnd

;/*
;** Define a DDA running macro.  We use eight specialized loops for ;
;** speed.  Trying to make it just one leads to too many tests and jumps. ;
;*/

;/*
;** define the various keywords
;*/

m?dda_huge macro
?dda_huge = 1
endm

m?dda_style macro
?dda_style = 1
endm

m?dda_rotate macro
?dda_rotate = 1
endm

m?dda_short macro
?dda_short = 1
endm

;/*
;** define the macro
;*/

run_the_dda     macro   mylist
        local   top_of_run
top_of_run:

;/*
;** determine what arguments we are given
;*/

?dda_huge = 0
?dda_style = 0
?dda_short = 0
?dda_rotate = 0
        irp     x,<mylist>
ifdef m?dda_&&x
        m?dda_&&x
else
        ??error2  <run_the_dda - unknown keyword x>
endif
        endm

;/*
;** handle the huge sidestep
;** WARNING: only set HUGE if cbSideStepAddressDelta is non-zero!
;*/

if ?dda_huge
        dec     wScan
        jnz     @F
        mov     ax,cScansPerSegment               ;; reset wScan
        mov     wScan,ax
        mov     ax,ds                             ;; advance the segment
        add     ax,selHugeDelta
        mov     ds,ax
        assumes ds,nothing
        add     di,cbHugeScanDelta               ;; wrap the surface address
@@:
endif ; ?dda_huge

;/*
;**; handle normal sidestep and rotate
;*/

if ?dda_rotate
        ror     bl,1
        adc     di,cbSideStepAddressDelta
else
        add     di,cbSideStepAddressDelta
endif ; ?dda_rotate

;/*
;** handle styling
;*/

if ?dda_style
        mov     cl,bActiveStyleCounter
        xor     ch,ch
        add     cx,wStyleStepSide
        mov     bActiveStyleCounter,cl
        mov     cl,ch
        and     cl,7
        rol     bActiveStyleMask,cl
endif ; ?dda_style

;/*
;** go for the next scan
;*/

        dec     cScans
if ?dda_short
        jz      do_the_last_scan
else
        njz     do_the_last_scan
endif

;/*
;** determine the count for intermediate scans
;*/

        mov     cx,cm
        mov     si,wError
        or      si,si
        js      @F
        inc     cx
        sub     si,lsg.lsg_cdy                    ;; wError -= 1
@@:     add     si,wFracS                         ;; wError += frac(s)
        mov     wError,si

;/*
;** loop back to the top, while calling the RunRoutine
;*/

        push    CodeOFFSET top_of_run
        jmp     npfnRunRoutine                    ; CX = number of pixels to draw
        endm

;/*
;** The table of DDA routines.                          
;*/

WDDA_ROTATE     equ     0002h
WDDA_STYLE      equ     0004h
WDDA_HUGE       equ     0008h

        .errnz  WDDA_ROTATE - 2
        .errnz  WDDA_STYLE  - 4
        .errnz  WDDA_HUGE   - 8

dda_table       equ     this word
        dw      dda____
        dw      dda___R
        dw      dda__S_
        dw      dda__SR
        dw      dda_H__
        dw      dda_H_R
        dw      dda_HS_
        dw      dda_HSR

;/***************************************************************************
;*
;* PUBLIC ROUTINE  polyline_workhorse_routine 
;*
;* DESCRIPTION   = the DDA algorithm used here is based on Jack Bresenham's Run 
;*                 Length Algorithm for Incremental Lines.  NATO ASI Series.    
;*                 Volume F17.  Fundamental Algorithms for Computer Graphics.   
;*                 Springer-Verlag Berlin Heidelberg 1985.                      
;*                                                                              
;*                 WARNING: this routine is called by DrawLines in DRAWLINE.ASM,                                                              
;*                       as well as by do_polyline.                                                                                           
;*                                                                              
;*                 Registers Destroyed:                                                                       
;*                       AX,BX,CX,DX,DI,SI                                                                    
;*                 Registers Preserved:                                                                       
;*                       DS,ES                                                                                
;*                                                                              
;* INPUT         = DI = cdx           
;*                 SI = cdy           
;*                 BP = polyline frame
;*                 
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   polyline_workhorse_routine,<PUBLIC,NEAR,NODATA,NONWIN>
cBegin
        mov     iWhichDDA,0

;/*
;** check for horizontal lines
;*/

        or      si,si
        jnz     non_horizontal
        mov     yA,si
        mov     cScans,si
        mov     ax,lsg.lsg_xB
        sub     ax,lsg.lsg_xA
        inc     ax
        mov     cB,ax
        jmp     know_my_dda
non_horizontal:

;/*
;** On a 16 MHz 386, all the following clip calculations take only
;** 15 microseconds.  This seems to indicate that we don't need a
;** special case for simpler clipping!
;*/

;/*
;** Compute the various slope quantities:             ;
;**                                                     ;
;**    m = floor(s)                                    (12) ;
;**                                                     ;
;**    wFracS = frac(s)        (in units of 1/dy)    ;
;*/

        mov     ax,di
        xor     dx,dx
        div     si
        mov     cm,ax
        mov     wFracS,dx

;/*
;** Compute starting scan, remember that (2 dx) might be 17 bits!  ;
;**                                                     ;
;**    y  = ceil((x - f )/s + 1/2) - 1                 ;
;**     a          a   0                                ;
;**                                                     ;
;**       = floor((x - f )/s + 1/2 - 1/(2 dx))       (27) ;
;**                 a   0                               ;
;**                                                     ;
;**       = floor((2 dy x  - 2 dy f  + dx - 1) / (2 dx))   ;
;**                      a                          0     ;
;*/

        add     si,si                             ; SI = (2 dy)
        mov     ax,lsg.lsg_xA                     ; 2 dy xA
        mul     si                                ; 
        mov     bx,di                             ; dx - 1
        dec     bx
        xor     cx,cx
        cmp     byte ptr lsg.lsg_fsLineFlags[1],1 ; -2 dy f   (i.e. +1 if flip)
        .errnz  LF_VERTICAL_FLIP - 100h            ;    0
        cmc
        adc     ax,bx
        adc     dx,cx
        shr     dx,1                              ; / 2
        rcr     ax,1
        adc     cx,cx
        div     di                                ; / dx
        mov     yA,ax                             ; remainder rm = 2 * DX + CX

;/*
;** Compute the first run:                              ;
;**                                                     ;
;**    c  = floor(s(y + 1/2) + f ) - x + 1            ;
;**     a            a          0     a                ;
;**                                                    (30) ;
;**                    rm + 1                           ;
;**       = floor( s - ------ ) + 1                    ;
;**                     2 dy                            ;
;*/

        mov     ax,di
        sub     ax,dx
        xor     dx,dx
        add     ax,ax
        adc     dx,dx
        inc     cx
        sub     ax,cx
        sbb     dx,0                              ; DX:AX = 2 dx - (rm + 1)
        div     si
        inc     ax
        mov     cA,ax

;/*
;** Compute the initial error term:                    ;
;**                                                     ;
;**    f' = frac(s(y + 1/2) + f ) + frac(s) - 1                           ;
;**     1           a          0                        ;
;**                                                    (31) ;
;**                   rm + 1                            ;
;**       = frac( s - ------ ) + frac(s) - 1.         ;
;**                    2 dy                             ;
;*/

;/*
;** DX is presently frac(s - (rm + 1)/(2 dy)) in units of 1/(2 dy).
;** If we were to express frac(s) and 1 in units of 1/(2 dy), they would
;** each be EVEN numbers.  Therefore the bottom bit of f' will be unchanged
;** whenever we calculate f' <- frac(f') + frac(s) - 1.  This bottom bit
;** is therefore wasted.  Since (f' >= 0) is unchanged if we turn off the
;** bottom bit, this is what we will do.           On the other hand, f' needs to
;** be a signed number and we therefore need the top bit to be significant.
;** The result of all this is that we will shift the bottom bit off of
;** the unsigned DX, and keep f' (wError) in units of 1/dy.
;*/

        shr     dx,1
        add     dx,wFracS
        sub     dx,lsg.lsg_cdy
        mov     wError,dx                         ; that was easy!

;/*
;** Compute the final scan, remember that (2 dx) might be 17 bits! ;
;**                                                     ;
;**    y  = ceil((x - f )/s + 1/2) - 1                (23) ;
;**     b          b   0                                ;
;**                                                     ;
;**       = floor((x - f )/s + 1/2 - 1/(2 dx))       (27) ;
;**                 b   0                               ;
;**                                                     ;
;**       = floor((2 dy x  - 2 dy f  + dx - 1) / (2 dx))   ;
;**                      b                          0     ;
;*/

        mov     ax,lsg.lsg_xB                     ; 2 dy xB
        mul     si
        mov     bx,di                             ; dx - 1
        dec     bx
        xor     cx,cx
        cmp     byte ptr lsg.lsg_fsLineFlags[1],1 ; -2 dy f
        .errnz  LF_VERTICAL_FLIP - 100h            ;    0
        cmc
        adc     ax,bx
        adc     dx,cx
        shr     dx,1                              ; / 2
        rcr     ax,1
        adc     cx,cx
        div     di                                ; / dx, remainder rm' = 2 * DX + CX

;/*
;** if both scans are the same, we're clipped to be horizontal
;*/

        sub     ax,yA
        mov     cScans,ax
        jnz     still_non_horizontal
        mov     ax,lsg.lsg_xB
        sub     ax,lsg.lsg_xA
        inc     ax
        mov     cB,ax
        jmp     know_my_dda
still_non_horizontal:

;/*
;** Compute the final run:                              ;
;**                                                     ;
;**    c  = x  - floor(s(y - 1/2) + f )                ;
;**     b    b            b                           0     ;
;**                                                    (32) ;
;**                   rm'+ 1           rm'+ 1         ;
;**       = - floor(- ------ ) = ceil( ------ )       ;
;**                    2 dy                              2 dy    ;
;*/

        mov     ax,dx
        xor     dx,dx
        add     ax,ax
        adc     dx,dx
        inc     cx
        add     ax,cx
        adc     dx,0
        div     si
        neg     dx                                ; round any remainder up
        adc     ax,0
        mov     cB,ax
know_my_dda:

;/*
;** Advance the style mask for clipping.               ;
;*/

        cmp     LineStyle,LINETYPE_SOLID-1
        jz      no_style_adjust
        or      iWhichDDA,WDDA_STYLE
        mov     ax,lsg.lsg_xA
        mov     bx,yA
        sub     ax,bx
        mul     wStyleStepRun
        xchg    ax,bx
        mul     wStyleStepSide
        add     ax,bx
        add     bActiveStyleCounter,al
        adc     ah,0
        mov     cl,ah
        and     cl,7
        rol     bActiveStyleMask,cl
no_style_adjust:

;/*
;** Rotate the clipped start point and sidestep back to real  ;
;** coordinates so we can calculate surface addresses.   ;
;*/

;/*
;** load the first pixel
;*/

        mov     bx,lsg.lsg_xA                      ; (BX,AX) = first pixel
        mov     ax,yA

;/*
;** Load a description of the side step.           This defaults to a (1,1) diagonal
;** step.  A step up requires subtraction of the scan size.  A step to the
;** right requires a bit rotation.  We hold this information as two flags:
;** CL = the bit rotation, CH = (BOOL) (do we move a scan?).  We supplement
;** this information with DX = CH * scansize.  We'll also carry along the
;** scan size since it must get negated if we are Y flipped.
;*/

        mov     si,cbScanSize
        neg     si                                ; default up => negative address delta
        mov     cx,101h                           ; CH = move by scan, CL = rotate bit

;/*
;** HALF FLIP
;*/

        mov     di,lsg.lsg_fsLineFlags           ; DI = flags
        test    di,LF_HALF_FLIP
        jz      @F
        sub     ax,bx                             ; y = x - y
        neg     ax
        xor     ch,ch
@@:

;/*
;** XY XCHG
;*/

        test    di,LF_XY_XCHG
        jz      @F
        xchg    ax,bx
        xchg    ch,cl
@@:

;/*
;** Y FLIP
;*/

        test    di,LF_Y_FLIP
        jz      @F
        neg     ax                                ; y = -y
        neg     si
@@:
        add     cl,cl
        .errnz  WDDA_ROTATE - 2
        or      byte ptr iWhichDDA,cl
        mov     cbScanAddressDelta,si
        neg     ch
        sbb     cx,cx
        and     si,cx
        mov     cbSideStepAddressDelta,si         ; SI = SideStep delta

;/*
;** Compute the surface address.                        ;
;*/

        add     bx,lsg.lsg_xStart                 ; BX = pixel in scan
        add     ax,lsg.lsg_yStart
        mov     dx,cySurface                      ; surface top is at low addresses
        dec     dx                                ;  => invert y coordinate
        sub     dx,ax                             ; DX = scan line

;/*
;** handle adjustment of huge surfaces
;*/

        mov     cx,cScansPerSegment
        jcxz    not_huge_surface
        or      si,si                             ; use: SI = SideStep delta
        jz      @F
        or      iWhichDDA,WDDA_HUGE
@@:     mov     si,pSurfaceStart.sel             ; SI = surface selector
        cmp     dx,cx
        jb      no_huge_adjust_needed
        mov     ax,dx
        xor     dx,dx
        div     cx                                ; DX = scan in final segment
        mov     ah,DOSHUGEINCR
        mul     ah
        add     si,ax
no_huge_adjust_needed:
        mov     pSurfaceAddress.sel,si

;/*
;** decide what the huge adjustments are
;*/

        sub     cx,dx                             ; CX = number of remaining scans
        mov     ax,cbHugeScanWrap                ;  in this segment
        mov     si,DOSHUGEINCR
        test    di,LF_Y_FLIP
        jnz     @F
        mov     cx,dx
        inc     cx
        neg     ax
        neg     si
@@:     mov     selHugeDelta,si
        mov     cbHugeScanDelta,ax
not_huge_surface:
        mov     wScan,cx                          ; number of remaining scans

;/*
;** compute the offset and bit mask
;*/

        mov     ax,cbScanSize
        mul     dx
        mov     cx,bx
        shr     bx,3
        add     ax,bx
        add     ax,pSurfaceStart.off
        mov     pSurfaceAddress.off,ax
        and     cl,7
        mov     al,80h
        ror     al,cl
        mov     bRotBitMask,al

;/*
;** Determine the Run Routine.                          ;
;*/

        mov     si,di
        and     si,110b
        .errnz  LF_HALF_FLIP + LF_XY_XCHG - 110b
        add     si,anpfnRunRoutines
        lods    word ptr cs:[si]
        mov     npfnRunRoutine,ax

;/*
;** Setup for a pass.                                   ;
;*/

;/*
;** copy all the variables that the DDA loop will destroy
;*/

        mov     ax,cPasses
        mov     cTmpPasses,ax
        mov     al,CurLineColor
        mov     TmpCurLineColor,al
do_another_pass:

;/*
;** load the surface address (we assume the segment is loaded)
;*/

        mov     di,pSurfaceAddress.off           ; DS:DI = surface address

;/*
;** call the per pass setup routine
;*/

        call    npfnPassSetup
        or      ax,ax
        jz      do_next_pass

;/*
;** Run the DDA!                                        ;
;*/

        mov     bl,bRotBitMask                    ; BL = rotating bit mask

;/*
;** send a single scan to the end
;*/

        cmp     cScans,0
        jz      do_the_last_scan

;/*
;** draw the first scan
;*/

        mov     cx,cA
        mov     si,iWhichDDA
        push    dda_table[si]
        jmp     npfnRunRoutine                    ; CX = number of pixels to draw

;/*
;** our favorite DDA routines
;*/

dda____:        run_the_dda                      <short>

dda___R:        run_the_dda                      <short,rotate>

;/*
;** Finish up the workhorse routine.                   ;
;*/

;/*
;** dump out the last scan
;*/

do_the_last_scan:
        mov     cx,cB
        call    npfnRunRoutine

;/*
;** see if we need another pass
;*/

do_next_pass:
        dec     cTmpPasses
        jz      workhorse_done
        jmp     do_another_pass
workhorse_done:

cEnd

;/*
;** More DDA routines.                                  ;
;*/

dda__S_:        run_the_dda                      <short    ,style >
dda__SR:        run_the_dda                      <short    ,style,rotate>
dda_H__:        run_the_dda                      <      huge  >
dda_H_R:        run_the_dda                      <      huge  ,rotate>
dda_HS_:        run_the_dda                      <      huge,style >
dda_HSR:        run_the_dda                      <      huge,style,rotate>

        public  device_unsetup

device_unsetup  proc    near
        mov     dx,EGA_BASE+GRAF_ADDR
        mov     ax,GRAF_ENAB_SR                  ; disable all planes for set_reset.
        out16   dx,ax
        mov     ax,DR_SET*256 + GRAF_DATA_ROT
        out16   dx,ax                             ; set, no rotation.
        mov     ax,0FF00h+GRAF_BIT_MASK
        out16   dx,ax                             ; enable all bits.
        mov     ax,MM_ALL                         ; enable all planes.
        mov     dl,SEQ_DATA
        out     dx,al
        ret
device_unsetup  endp

        public  device_one_pass_init

;/***************************************************************************
;*
;* PUBLIC ROUTINE  device_one_pass_init 
;*
;* DESCRIPTION   = Sets up the ega to handle one of the twelve raster        
;*                 operations which can be done in one pass of EGA memory.   
;*                                                                           
;*                 Registers Destroyed:                                                               
;*                       AX,BX,DX                                                                     
;*                 Registers Preserved:                                                               
;*                       CX,DI,SI,BP,DS,ES  
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                         ---Pseudo-Code--
;*  device_one_pass_init
;*  {
;*     // this routine sets up the EGA to handle one of the twelve
;*     // raster operations which can be done in one pass of EGA memory.
;* 
;*     // set up the set/reset register.
;* 
;*     TmpColor &= PenANDFlag;
;*     TmpColor ^= PenXORFlag;
;*     set set/reset register to TmpColor;
;* 
;*     enable all planes for set/reset;
;* 
;*     set data rotate register to DataROTFlag;
;* 
;*     // the bitmask register must be the last register written to the
;*     // graphics controller so that its address is still in the
;*     // address register.
;* 
;*     enable all bits in bitmask register;
;* 
;*     enable all planes for writing in the sequencer;
;* 
;*     return();
;*  }
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

device_one_pass_init    proc                     near
        mov     bx,DrawModeIndex
        mov     ah,CurLineColor
        and     ah,Code_rop_pen_and[bx]
        xor     ah,Code_rop_pen_xor[bx]
        mov     al,GRAF_SET_RESET                ; Write to set/reset register
        mov     dx,EGA_BASE + GRAF_ADDR
        out16   dx,ax

        mov     ah,MM_ALL                         ; enable all planes for set_reset
        mov     al,GRAF_ENAB_SR
        out16   dx,ax

        mov     ah,Code_rop_data_r[bx]            ; Set DataRotate register value
        mov     al,GRAF_DATA_ROT
        out16   dx,ax

;/*
;** The bitmask register must be the last register written to the
;** Graphics Controller so that it's address is still in the
;** address register.
;*/

set_bit_mask:
        mov     ax,0FF00h+GRAF_BIT_MASK
        out16   dx,ax

        mov     al,MM_ALL                         ; enable all planes for writing
        mov     dl,SEQ_DATA
        out     dx,al
        ret
device_one_pass_init    endp

        public  device_one_pass

        assumes ds,nothing
        assumes es,nothing

device_one_pass         proc                     near
        mov     dx,EGA_BASE + GRAF_DATA
        mov     ax,1
        ret
device_one_pass         endp

        public  device_first_pass_of_two

;/***************************************************************************
;*
;* PUBLIC ROUTINE   device_first_pass_of_two
;*
;* DESCRIPTION   =  Sets up the EGA for the four raster operations which    
;*                  take two passes of EGA memory.  These raster operations 
;*                  are all handled in a similar fashion:  on the first pass
;*                  we set the necessary bits of the necessary planes to    
;*                  ones or zeros explicitly.  The second pass we invert the
;*                  destination bits for the remaining planes.              
;*
;*                  Registers Destroyed:     
;*                        BX                 
;*                  Registers Preserved:     
;*                        CX,DI,SI,BP,DS,ES  
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1                    
;*                 DX = EGA_BASE + GRAF_DATA 
;* RETURN-ERROR  = NONE
;*
;*                       ----Pseudo-Code-----
;* BOOL device_first_pass_of_two
;* {
;*    // this routine sets up the EGA for the four raster operations which
;*    // take two passes of EGA memory.  these raster operations are all
;*    // handled in a similar fashion: on the first pass we set the necessary
;*    // bits of the necessary planes to ones or zeros explicitly. the second
;*    // pass we invert the destination bits for the remaining planes.
;*
;*    set set/reset register to PenANDFlag;
;*
;*    set TmpColor to the current pen or background color;
;*    TmpColor ^= PenXORFlag;            // invert the color if necessary.
;*    TmpColor &= MM_ALL;
;*
;*    // enable only the planes that need it.
;*
;*    set enable set/reset register to TmpColor;
;*
;*    set data rotate register to DataROTFlag;
;*
;*    // the bitmask register must be the last register written to the
;*    // graphics controller so that its address is still in the
;*    // address register.
;*
;*    enable all bits in bit mask register;
;*
;*    reset TmpColor to current pen or background color;
;*    TmpColor ^= PenXORFlag;            // invert color if necessary.
;*
;*    // enable only necessary planes for writing.
;*
;*    set sequencer data register to TmpColor;
;*
;*    return();
;* }
;*
;***************************************************************************/

        assumes ds,nothing
        assumes es,nothing

device_first_pass_of_two                         proc near

;/*
;** make copies of variables that a pass will destroy
;*/

        mov     ax,cScans
        mov     cTmpScans,ax
        mov     ax,wError
        mov     wTmpError,ax
        mov     al,bActiveStyleCounter
        mov     bTmpActiveStyleCounter,al
        mov     al,bActiveStyleMask
        mov     bTmpActiveStyleMask,al

;/*
;** setup the EGA
;*/

        mov     bx,DrawModeIndex
        mov     ah,Code_rop_pen_and[bx]
        mov     al,GRAF_SET_RESET                ;Write to set/reset register
        mov     dx,EGA_BASE + GRAF_ADDR
        out16   dx,ax

        mov     ah,CurLineColor                  ; get the color.
        xor     ah,Code_rop_pen_xor[bx]          ; invert color if necessary.
        and     ah,MM_ALL                        ; enable only the planes that need it.
        mov     al,GRAF_ENAB_SR
        out16   dx,ax

        mov     ah,Code_rop_data_r[bx]           ;Set DataRotate register value
        mov     al,GRAF_DATA_ROT
        out16   dx,ax

;/*
;** The bitmask register must be the last register written to the
;** Graphics Controller so that it's address is still in the
;** address register.
;*/

        mov     ax,0FF00h+GRAF_BIT_MASK
        out16   dx,ax

        mov     al,CurLineColor                   ; get the color.
        xor     al,Code_rop_pen_xor[bx]           ; invert color if necessary.
        and     al,MM_ALL                         ; enable only necessary planes for writing.
        mov     dl,SEQ_DATA
        out     dx,al
        mov     dl,GRAF_DATA
        mov     npfnPassSetup,CodeOFFSET device_second_pass_of_two
        mov     ax,1
        ret
device_first_pass_of_two                         endp

        public  device_second_pass_of_two

;/***************************************************************************
;*
;* PUBLIC ROUTINE   device_second_pass_of_two 
;*
;* DESCRIPTION   =  We have one of the four raster operations which cannot be 
;*                  done in one pass of EGA memory.  The first pass will have 
;*                  set the necessary bits in the necessary planes to either  
;*                  1's or 0's explicitly.  This pass will NOT the destination
;*                  for the necessary bits in the necessary planes.  The EGA  
;*                  has to be set up to not the destination, before the line  
;*                  is drawn.                                                 
;*
;*                  Registers Destroyed:    
;*                        BX                
;*                  Registers Preserved:    
;*                        CX,DI,SI,BP,DS,ES 
;*
;* INPUT         = AX = 1   if the line needs to be drawn                                                                    
;*                 AX = 0   if the line does not need to be drawn 
;*                 DX = EGA_BASE + GRAF_DATA                      
;*                 
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                      -------Pseudo-Code-----
;*   BOOL device_second_pass_of_two
;*   {
;*      TmpColor = current pen or background color;
;*      TmpColor ^= PenXORFlag;            // invert color if necessary.
;*      TmpColor = ! TmpColor;             // get the other planes.
;*  
;*      // enable the planes not enabled in first pass.
;*  
;*      set sequencer data register to TmpColor;
;*  
;*      set EGA to XOR mode;
;*  
;*      enable all bits in bit mask register;
;*   }
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

device_second_pass_of_two                        proc near

;/*
;** restore variables destroyed by the first pass
;*/

        mov     ax,cTmpScans
        mov     cScans,ax
        mov     ax,wTmpError
        mov     wError,ax
        mov     al,bTmpActiveStyleCounter
        mov     bActiveStyleCounter,al
        mov     al,bTmpActiveStyleMask
        mov     bActiveStyleMask,al

;/*
;** setup the EGA for pass 2
;*/

        mov     al,CurLineColor                   ; get the color
        mov     bx,DrawModeIndex
        xor     al,Code_rop_pen_xor[bx]           ; invert color if necessary
        not     al                                ; enable the other planes
        and     ax,MM_ALL
        jz      standard_ega_get_out              ; no bits to XOR

        mov     dx,EGA_BASE+SEQ_DATA
        out     dx,al

        mov     dl,GRAF_ADDR                      ; set to XOR mode
        mov     ax,DR_XOR * 256 + GRAF_DATA_ROT
        out16   dx,ax

        mov     ax,0FF00h+GRAF_BIT_MASK           ; set up bitmask register
        out16   dx,ax

        mov     dl,GRAF_DATA
        mov     ax,1
standard_ega_get_out:
        mov     npfnPassSetup,CodeOFFSET device_first_pass_of_two
        ret
device_second_pass_of_two                        endp

        public  bitmap_pass_per_plane

;/***************************************************************************
;*
;* PUBLIC ROUTINE  bitmap_pass_per_plane
;*
;* DESCRIPTION   = This routine is called before each pass over a bitmap.  With
;*                 bitmaps we can break the operation down into four           
;*                 categories.  Set some bits to zero, set some bits to one,   
;*                 xor some bits, or do nothing.  Which operation to be used is
;*                 determined by the raster operation, and whether or not the  
;*                 current bits in the current color plane are set.            
;*                 dwBitmapROP breaks the operation into two pieces.  If the   
;*                 low byte is FF, we first turn OFF all the bits indicated.   
;*                 If the high byte is FF, we XOR the bits indicated.  This    
;*                 produces all four operations correctly.  Obviously, if      
;*                 dwBitmapROP is 0000, there is no need to draw the line in   
;*                 this pass.                                                  
;*                 
;*                 Registers Destroyed: 
;*                       BX             
;*                 Registers Preserved: 
;*                       CX,DI,SI,BP,ES 
;*                 
;* INPUT         = AX = 1   if the line needs to be drawn         
;*                 AX = 0   if the line does not need to be drawn 
;*                 DS = bitmap selector (set here if huge)        
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

bitmap_pass_per_plane   proc                     near

;/*
;** save any variables we're going to destroy
;*/

        mov     ax,cPlanes
        cmp     ax,1
        jz      bitmap_vars_all_saved
        cmp     cTmpPasses,ax
        jnz     restore_variables
        mov     ax,cScans
        mov     cTmpScans,ax
        mov     ax,wError
        mov     wTmpError,ax
        mov     al,bActiveStyleCounter
        mov     bTmpActiveStyleCounter,al
        mov     al,bActiveStyleMask
        mov     bTmpActiveStyleMask,al
        mov     ax,wScan
        mov     wTmpScan,ax
        jmp     short bitmap_vars_all_saved

;/*
;** on later passes, restore destroyed variables
;*/

restore_variables:
        mov     ax,cTmpScans
        mov     cScans,ax
        mov     ax,wTmpError
        mov     wError,ax
        mov     al,bTmpActiveStyleCounter
        mov     bActiveStyleCounter,al
        mov     al,bTmpActiveStyleMask
        mov     bActiveStyleMask,al
        mov     ax,wTmpScan
        mov     wScan,ax
bitmap_vars_all_saved:

;/*
;** Advance the start address to the next plane.   This doesn't have any
;** effect until the next pass.
;*/

        mov     ax,cbNextPlane
        add     pSurfaceAddress.off,ax

;/*
;** load the starting selector if this is a huge bitmap
;*/

        cmp     cScansPerSegment,0
        jz      not_huge
        mov     ds,pSurfaceAddress.sel
        assumes ds,nothing
not_huge:

;/*
;** determine the logical operation to do
;*/

        mov     bx,DrawModeIndex                 ; get the raster op
        ror     TmpCurLineColor,1                ; see if the color plane is set
        jnc     set_the_standard_proc
        shr     bx,2
set_the_standard_proc:
        and     bx,3
        add     bx,bx                             ; make a word ptr
        mov     ax,bm_and_xor_table[bx]           ; add index into table
        mov     wBitmapROP,ax                     ; set up write logic
        ret
bitmap_pass_per_plane   endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE   dev_polyline_init 
;*
;* DESCRIPTION   =  Initializes stack frame variables for drawing lines to the 
;*                  display. 
;*                 
;*                  Registers Destroyed:   
;*                        AX,BX,CX,DX,SI,DI
;*                  Registers Preserved:   
;*                        BP               
;*                  Calls:
;*                        load_color_info
;*                        polyline_set_ega_for_one_pass
;*                        polyline_set_ega_for_two_pass
;*
;* INPUT         = DS:DI => DDC    (ignored)   
;* OUTPUT        = DS = EGA selector  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

dev_polyline_init       proc                     near

;/*
;** Depending on whether the raster operation coming in can be done in one pass
;** of EGA memory or not, we set up the EGA differently.
;*/

        mov     bx,DrawModeIndex
        mov     bl,Code_rop_flags[bx]
        mov     cx,2
        mov     ax,CodeOFFSET device_first_pass_of_two
        test    bl,SINGLE_OK
        jz      better_do_double
        call    device_one_pass_init
        mov     cx,1
        mov     ax,CodeOFFSET device_one_pass
better_do_double:
        mov     cPasses,cx
        mov     npfnPassSetup,ax
        mov     npfnUnSetup,CodeOFFSET device_unsetup

;/*
;** decide which set of run routines to use
;*/

        mov     ax,CodeOFFSET dev_styled_table
        cmp     LineStyle,LINETYPE_SOLID - 1
        jnz     @F
        mov     ax,CodeOFFSET dev_solid_table
@@:     mov     anpfnRunRoutines,ax

;/*
;** record the surface parameters
;*/

        mov     cySurface,SCREEN_CY
        mov     cbScanSize,SCREEN_CBSCAN
        mov     cScansPerSegment,0
        mov     ax,sdScreen.sd_pBits.off
        mov     ds,sdScreen.sd_pBits.sel
        assumes ds,nothing
        mov     pSurfaceStart.off,ax
        mov     pSurfaceStart.sel,ds
        ret
dev_polyline_init       endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE  bm_polyline_init  
;*
;* DESCRIPTION   = Initializes stack frame variables for drawing lines to a bitmap.
;*
;*                 Registers Destroyed:  
;*                       AX,BX,CX        
;*                 Registers Preserved:  
;*                       DX,DI,SI,BP,ES  
;*                 Calls:
;*                       get_bm_info
;*
;* INPUT         = DS:DI = DDC 
;* OUTPUT        = DS = first segment of bitmap  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

bm_polyline_init        proc                     near
        call    get_bm_info                       ; takes DS:DI = DDC
        assumes ds,nothing

;/*
;** decide which set of run routines to use
;*/

        mov     ax,CodeOFFSET bitmap_solid_table
        cmp     LineStyle,LINETYPE_SOLID - 1
        jz      @F
        mov     ax,CodeOFFSET bitmap_styled_table
@@:     mov     anpfnRunRoutines,ax

;/*
;** set the routines to run on each pass
;*/

        mov     ax,cPlanes
        mov     cPasses,ax
        mov     npfnPassSetup,CodeOFFSET bitmap_pass_per_plane
        mov     npfnUnSetup,CodeOFFSET NullRoutine
        ret
bm_polyline_init        endp

        public  NullRoutine

NullRoutine     proc    near
        ret                                       
NullRoutine     endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_bm_info       
;*
;* DESCRIPTION   =  Loads bitmap information from the DDC into local variables.
;*
;*                 Registers Destroyed:  
;*                       AX,BX,CX        
;*                 Registers Preserved:  
;*                       DX,DI,SI,BP,ES  
;*
;* INPUT         = DS:DI => DDC  
;* OUTPUT        = DS = first segment of bitmap  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

get_bm_info     proc    near
        ddc?    di

;/*
;** Color bitmaps are assumed to be four planes deep.
;*/

        mov     bx,[di].ddc_npsd                  ; get ptr to surface definition
        mov     cx,BITS_PEL                       ; assume color
        .errnz  BITS_PEL - 4
        test    [bx].sd_fb,SD_COLOR
        jnz     @F
        mov     cl,1                              ; nope, we're mono
        shr     CurLineColor,4                    ; move mono bit to lsb
        .errnz  MONO_BIT - 16                     ; 16 = 2^4
@@:     mov     cPlanes,cx                        ; CX = number of planes

        mov     ax,[bx].sd_cy
        mov     cySurface,ax                      ; height of destination

;/*
;** Bitmaps are stored in groups of scan lines.  For example, with three color
;** planes, the bitmap is stored as a scan line of the first color plane, a scan
;** line of the second color plane, a scan line of the third color plane, a scan
;** line of the first color plane, and so on. Therefore, when writing to a color
;** plane, a move to the next scan line actually has to jump over three scan
;** lines.
;*/

        mov     ax,[bx].sd_cbScan                 ; the next plane starts with the
        mov     cbNextPlane,ax                    ;   next scan line
        mul     cx                                ; compute where the next scan begins
        mov     cbScanSize,ax

;/*
;** Check to see if we have a small or a huge bitmap.  If it is huge, then this
;** is a good time to load huge bitmap information.  If it is small, skip this
;** section.
;*/

        xor     ax,ax
        test    [bx].sd_fb,SD_HUGE
        jz      get_bm_info_exit
        mov     ax,[bx].sd_cbFill
        add     ax,[bx].sd_pBits.off
        mov     cbHugeScanWrap,ax
        mov     ax,[bx].sd_cySeg
get_bm_info_exit:
        mov     cScansPerSegment,ax              ; zero for non-huge bitmap
        mov     ax,[bx].sd_pBits.off
        mov     pSurfaceStart.off,ax             ; offset of memory bitmap
        mov     cx,[bx].sd_pBits.sel
        lar     ax,cx
        jz      @F
        cCall   DosGetSeg,<cx>
@@:     mov     ds,cx
        assumes ds,nothing
        mov     pSurfaceStart.sel,cx             ; segment of memory bitmap
        ret
get_bm_info     endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE  polyline_init 
;*
;* DESCRIPTION   = Do common initialization for polylines and scanlines.     
;*
;*                  Registers Destroyed:        
;*                        AX,BX                 
;*                  Registers Preserved:        
;*                        CX,DX,SI,DI,BP,DS,ES  
;*
;* INPUT         = DS:DI = DDC
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

cProc   polyline_init,<PUBLIC,NEAR,NODATA,NONWIN>
cBegin
        call    get_line_attributes

;/*
;** the surface information (and maybe hardware setup)
;*/

        mov     bx,CodeOFFSET bm_polyline_init
        test    [di].ddc_fb,DDC_DEVICE
        jz      @F
        mov     bx,CodeOFFSET dev_polyline_init
@@:     call    bx
cEnd

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_line_attributes 
;*
;* DESCRIPTION   = Initialize local variables with line color and style,     
;*                 and raster operation.                                   
;*                                                                          
;*                 Registers Destroyed:        
;*                       AX,BX                 
;*                 Registers Preserved:        
;*                       CX,DX,SI,DI,BP,DS,ES  
;*                
;* INPUT         = DS:DI = DDC
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

get_line_attributes     proc                     near
        ddc?    di

;/*
;** Get line color
;*/

        mov     al,[di].ddc_la.la_ba.ba_ipc.ipc_bClr
        mov     CurLineColor,al

;/*
;** get line style
;*/

        mov     bx,[di].ddc_la.la_usType
        mov     LineStyle,bl                      

        add     bx,bx
        mov     ax,style_table[bx]                ; a style mask from the table, and
        mov     wStyleMask,ax                     ; set the style flags.

        mov     ax,word ptr [di].ddc_la.la_bStepX ; AL=step x,AH=step y
        .errnz  la_bStepY - la_bStepX - 1
        xor     bx,bx
        xchg    bl,ah                             ; AX = X step, BX = Y step
        mov     wStyleStepHorz,ax
        mov     wStyleStepVert,bx

;/*
;** handle LINETYPE_ALTERNATE specially
;*/

        cmp     LineStyle,LINETYPE_ALTERNATE - 1
        jnz     @F
        mov     ax,100h
        mov     wStyleStepHorz,ax
        mov     wStyleStepVert,ax
@@:

        mov     ax,word ptr [di].ddc_la.la_bError ; AL=error, AH=mask position
        .errnz  la_bMask - la_bError - 1
        mov     wStyleCounter,ax

;/*
;** Get the raster operation to use
;*/

        mov     bl,[di].ddc_la.la_ba.ba_bmix
        and     bx,RASTER_OP_MASK                ;  (play it safe)
        mov     DrawModeIndex,bx                 ;  and save a copy
        ret
;/*
;** Now do
;*/


get_line_attributes     endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE  polyline_background_init  
;*
;* DESCRIPTION   = Do common initialization for polylines and scanlines. 
;*
;*                  Registers Destroyed:       
;*                        AX,BX                
;*                  Registers Preserved:       
;*                        CX,DX,SI,DI,BP,DS,ES 
;*
;* INPUT         = DS:DI = DDC  
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
 

cProc   polyline_background_init,<PUBLIC,NEAR,NODATA,NONWIN>
cBegin
        call    get_background_line_attributes

;/*
;** the surface information (and maybe hardware setup)
;*/

        mov     bx,CodeOFFSET bm_polyline_init
        test    [di].ddc_fb,DDC_DEVICE
        jz      @F
        mov     bx,CodeOFFSET dev_polyline_init
@@:     call    bx
cEnd


;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_background_line_attributes  
;*
;* DESCRIPTION   = Initialize local variables with line color and style, and 
;*                 raster operation.                                         
;*                                                                           
;*                 Registers Destroyed:                                                                  
;*                       AX,BX                                                                           
;*                 Registers Preserved:        
;*                       CX,DX,SI,DI,BP,DS,ES  
;*
;* INPUT         = DS:DI = DDC  
;* OUTPUT        = AL = sd_flags  (ddc_npsd.sd_fb)  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

get_background_line_attributes                   proc near
        ddc?    di

;/*
;** Get line color
;*/

        mov     ax,word ptr [di].ddc_la.la_ba.ba_ipcBack.ipc_bClr
        .errnz  ipc_bStatus - ipc_bClr - 1
        mov     CurLineColor,al

;/*
;** get line style
;*/

        mov     bx,[di].ddc_la.la_usType

;/*
;** The only thing that LineStyle is used for is special casing for
;** LINETYPE_SOLID and LINETYPE_ALTERNATE.  If LINETYPE_SOLID is the current
;** type, we won't ever come here.  If LINETYPE_ALTERNATE is the current line
;** type, we want the background to be drawn as LINETYPE_ALTERNATE too.  We
;** could set LineStyle to represent LINETYPE_SOLID if the foreground were
;** LINETYPE_INVISIBLE, but this is such a rare case that it is probably
;** unnecessary to worry about its efficiency.
;*/


        add     bx,bx
        mov     ax,style_table[bx]                ; a style mask from the table, and
        not     ax
        mov     wStyleMask,ax                     ; set the style flags.

        mov     ax,word ptr [di].ddc_la.la_bError ; AL=error, AH=mask position
        .errnz  la_bMask - la_bError - 1
        mov     wStyleCounter,ax

;/*
;** Get the raster operation to use
;*/

        mov     bl,[di].ddc_la.la_ba.ba_bkmix
        and     bx,RASTER_OP_MASK                ;  (play it safe)
        mov     DrawModeIndex,bx                 ;  and save a copy
        ret

get_background_line_attributes                   endp

sEnd    Code

        end
