;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; 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
;*              polyline_init
;*              get_line_attributes
;*              polyline_background_init
;*              get_background_line_attributes
;*
;* 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 polyline.inc
        include njmp.mac
        include assert.mac
        .list

        public  style_table

        externFP        far_clip_line              ; CLIP.ASM

sBegin  Code
        assumes cs,Code
        externW     CodeData

        externNP        get_bm_info                ; PLBMSUP.ASM
        externNP        bm_polyline_init
        externNP        dev_polyline_init
        externNP        polyline_workhorse_routine ; PLDDA.ASM

;/*
;** 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
ifdef FIREWALLS
        push    di
        PUSH    ES
        push    ss
        pop     es
        lea     di,lsg
        mov     cx,SIZE LINESEGMENT
        mov     ax,0CCCCh
        rep     stosb
        POP     ES
        pop     di
endif

        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
no_clip_rect:

;/*
;** Loop through the lines                        
;*/
        public  walk_thru_polylines

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                            ;
;*/

publab  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                                ;
;*/

publab  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                  ;
;*/

publab  clip_off_first_pel

        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                ;
;*/

publab  compute_line_style

        cmp     LineStyle,LINETYPE_SOLID - 1
        njz     ignore_styles

;/*
;**----------start of max metric changes -----------------
;*/

        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 -----------------
;*/

;/*
;** 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               ;
;*/

publab  do_half_flip

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       ;
;*/

publab  call_the_workhorse

        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    ; Leavealone
        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

;/***************************************************************************
;*
;* 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     ax,word ptr [di].ddc_la.la_ba.ba_ipc.ipc_bClr
        .errnz  ipc_bStatus - ipc_bClr - 1
        mov     CurLineColor,al
        mov     CurLineColorStatus,ah

;/*
;** 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
        assert  bl,B,10h
        and     bx,RASTER_OP_MASK                ;  (play it safe)
        mov     DrawModeIndex,bx                 ;  and save a copy
        mov     bx,[di].ddc_npsd                 ; get ptr to surface definition
        mov     al,[bx].sd_fb
        mov     sd_flags,al
        ret
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
