/**********************************Module*Header*******************************\
*  Module Name: Strips.c
* 
*  Hardware line drawing support routines
* 
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\******************************************************************************/
#include "precomp.h"
#include "gdi.h"

#define STRIP_LOG_LEVEL 6

//-----------------------------------------------------------------------------
//
//  BOOL bInitializeStrips
// 
//  Setup hardware for sucessive calls to strips functions.
// 
//-----------------------------------------------------------------------------
BOOL
bInitializeStrips(PDev*       ppdev,
                  ULONG       ulSolidColor, // Solid color fill
                  DWORD       dwLogicOp,    // Logical Operation to perform
                  RECTL*      prclClip)     // Clip region (Or NULL if no clip)
{
    DWORD       dwColorReg;
    BOOL        bRC = FALSE;
    Surf*       psurfDst = ppdev->psurf;
    ULONG*      pBuffer;

    PERMEDIA_DECL;

    DBG_GDI((STRIP_LOG_LEVEL + 1, "bInitializeStrips"));
    
    InputBufferReserve(ppdev, 16, &pBuffer);

    pBuffer[0] = __Permedia2TagFBWindowBase;
    pBuffer[1] =  psurfDst->ulPixOffset;

    pBuffer += 2;

    if ( dwLogicOp == K_LOGICOP_COPY )
    {
        dwColorReg = __Permedia2TagFBWriteData;

        pBuffer[0] = __Permedia2TagLogicalOpMode;
        pBuffer[1] =  __PERMEDIA_CONSTANT_FB_WRITE;
        pBuffer[2] = __Permedia2TagFBReadMode;
        pBuffer[3] =  PM_FBREADMODE_PARTIAL(psurfDst->ulPackedPP)
                   | PM_FBREADMODE_PACKEDDATA(__PERMEDIA_DISABLE);

        pBuffer += 4;
    }
    else
    {
        DWORD   dwReadMode;
        // Special case for 3DS Max when page flipping. Max uses an XOR'ed GDI
        // line within the 3d window. When pageflipping we double write GDI and
        // so always write to buffer 0. We need to make sure the frame buffer
        // read happens from the currently displayed buffer.
        //
        dwColorReg = __Permedia2TagConstantColor;
        dwReadMode = psurfDst->ulPackedPP | LogicopReadDest[dwLogicOp];

        pBuffer[0] = __Permedia2TagColorDDAMode;
        pBuffer[1] =  __COLOR_DDA_FLAT_SHADE;
        pBuffer[2] = __Permedia2TagLogicalOpMode;
        pBuffer[3] =  P2_ENABLED_LOGICALOP(dwLogicOp);
        pBuffer[4] = __Permedia2TagFBReadMode;
        pBuffer[5] =  dwReadMode;

        pBuffer += 6;

        //
        // We have changed the DDA Mode setting so we must return TRUE so we can
        // re-set it later.
        //
        bRC = TRUE;
    }

    pBuffer[0] = dwColorReg;
    pBuffer[1] = ulSolidColor;

    pBuffer += 2;

    if ( prclClip )
    {
        pBuffer[0] = __Permedia2TagScissorMode;
        pBuffer[1] = SCREEN_SCISSOR_DEFAULT | USER_SCISSOR_ENABLE;
        pBuffer[2] =__Permedia2TagScissorMinXY;
        pBuffer[3] = ((prclClip->left) << SCISSOR_XOFFSET)
                   | ((prclClip->top) << SCISSOR_YOFFSET);
        pBuffer[4] =__Permedia2TagScissorMaxXY;
        pBuffer[5] = ((prclClip->right) << SCISSOR_XOFFSET)
                   | ((prclClip->bottom) << SCISSOR_YOFFSET);

        pBuffer += 6;
        //
        // Need to reset scissor mode
        //
        bRC = TRUE;
    }
                     
    InputBufferCommit(ppdev, pBuffer);

    DBG_GDI((STRIP_LOG_LEVEL + 1, "bInitializeStrips done return %d", bRC));

    return(bRC);
}// bInitializeStrips()

//-----------------------------------------------------------------------------
//
//  VOID vResetStrips
// 
//  Resets the hardware to its default state
// 
//-----------------------------------------------------------------------------
VOID
vResetStrips(PDev* ppdev)
{
    ULONG*      pBuffer;

    DBG_GDI((STRIP_LOG_LEVEL + 1, "vResetStrips"));
    
    //
    // Reset hardware to default state
    //
    InputBufferReserve(ppdev, 4 , &pBuffer);

    pBuffer[0] = __Permedia2TagScissorMode;
    pBuffer[1] =  SCREEN_SCISSOR_DEFAULT;
    pBuffer[2] = __Permedia2TagColorDDAMode;
    pBuffer[3] =  __PERMEDIA_DISABLE;

    pBuffer += 4;

    InputBufferCommit(ppdev, pBuffer);

}// vResetStrips()

//-----------------------------------------------------------------------------
//
//  BOOL bFastIntegerLine
// 
//  Integer line drawing.
// 
//  Returns FALSE if the line can not be drawn due to hardware limitations
// 
//  NOTE: This algorithm is not completely compliant. Lines > 190 pixels long
//  may get some incorrect pixels plotted somewhere along the length.
//  If we detect these long lines then we fail the call.
//  NOTE: GLICAP_NT_CONFORMANT_LINES will always be set.
// 
//-----------------------------------------------------------------------------
BOOL
bFastIntegerLine(PDev*   ppdev,
                 LONG    X1,
                 LONG    Y1,
                 LONG    X2,
                 LONG    Y2)
{
    LONG dx, dy, adx, ady;
    LONG gdx, gdy, count;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "bFastIntegerLine"));

    //
    // Convert points to INT format
    //
    X1 >>= 4;
    Y1 >>= 4;
    X2 >>= 4;
    Y2 >>= 4;

    //
    // Get deltas and absolute deltas
    //
    if ( (adx = dx = X2 - X1) < 0 )
    {
        adx = -adx;
    }

    if ( (ady = dy = Y2 - Y1) < 0 )
    {
        ady = -ady;
    }

    if ( adx > ady )
    {
        //
        // X Major line
        //
        gdx  = (dx > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);

        if ( ady == 0 )
        {
            //
            // Horizontal lines
            //
            gdy = 0;
        }// if (ady == 0)
        else
        {
            //
            // We dont necessarily want to push any lines through Permedia2 that
            // might not be conformant
            //
            if ( (adx > MAX_LENGTH_CONFORMANT_INTEGER_LINES)
               &&(permediaInfo->flags & GLICAP_NT_CONFORMANT_LINES) )
            {
                return(FALSE);
            }

            gdy = INTtoFIXED(dy); 

            //
            // Need to explicitly round delta down for -ve deltas.
            //
            if ( dy < 0 )
            {
                gdy -= adx - 1;
            }

            gdy /= adx;
        }// if (ady != 0)
        count = adx;
    }// if ( adx > ady )
    else if ( adx < ady )
    {
        //
        // Y Major line
        //
        gdy  = (dy > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);

        if ( adx == 0 )
        {
            //
            // Vertical lines
            //
            gdx = 0;
        }
        else
        {
            //
            // We dont necessarily want to push any lines through Permedia2 that
            // might not be conformant
            //
            if ( (ady > MAX_LENGTH_CONFORMANT_INTEGER_LINES)
               &&(permediaInfo->flags & GLICAP_NT_CONFORMANT_LINES) )
            {
                return(FALSE);
            }

            gdx = INTtoFIXED(dx); 

            //
            // Need to explicitly round delta down for -ve deltas.
            //
            if ( dx < 0 )
            {
                gdx -= ady - 1;
            }

            gdx /= ady; 
        }
        count = ady;
    }// if ( adx < ady )
    else
    {
        //
        // Special case for 45 degree lines. These are always conformant.
        //
        gdx  = (dx > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);
        gdy  = (dy > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);
        count = adx;        
    }

    InputBufferReserve(ppdev, 16, &pBuffer);

    //
    // Set up the start point
    //
    pBuffer[0] = __Permedia2TagStartXDom;
    pBuffer[1] =  INTtoFIXED(X1) + NEARLY_HALF;
    pBuffer[2] = __Permedia2TagStartY;
    pBuffer[3] =  INTtoFIXED(Y1) + NEARLY_HALF;
    pBuffer[4] = __Permedia2TagdXDom;
    pBuffer[5] =  gdx;
    pBuffer[6] = __Permedia2TagdY;
    pBuffer[7] =  gdy;
    pBuffer[8] = __Permedia2TagCount;
    pBuffer[9] =  count;
    pBuffer[10] = __Permedia2TagRender;
    pBuffer[11] =  __RENDER_LINE_PRIMITIVE;
    
    pBuffer[12] = __Permedia2TagdXDom;
    pBuffer[13] =  0;
    pBuffer[14] = __Permedia2TagdY;
    pBuffer[15] =  INTtoFIXED(1);

    pBuffer += 16;

    InputBufferCommit(ppdev, pBuffer);

    DBG_GDI((STRIP_LOG_LEVEL + 1, "bFastIntegerLine Done"));

    return(TRUE);
}// bFastIntegerLine()

//-----------------------------------------------------------------------------
//
//  BOOL bFastIntegerContinueLine
// 
//  Integer line drawing through Permedia2.
// 
//  Returns FALSE if the line can not be drawn due to hardware limitations.
// 
//  NOTE: This algorithm is not completely compliant. Lines > 190 pixels long
//  may get some incorrect pixels plotted somewhere along the length.
//  If we detect these long lines then we fail the call.
//  NOTE: GLICAP_NT_CONFORMANT_LINES will always be set.
// 
//-----------------------------------------------------------------------------
BOOL
bFastIntegerContinueLine(PDev*   ppdev,
                         LONG    X1,
                         LONG    Y1,
                         LONG    X2,
                         LONG    Y2)
{
    LONG dx, dy, adx, ady;
    LONG gdx, gdy, count;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL + 1, "bFastIntegerContinueLine"));

    //
    // This assumes that the end point of the previous line is correct.
    // The Fraction adjust should be set to nearly a half to remove any
    // error from the end point of the previous line.
    // Get deltas and absolute deltas from 28.4 format
    //
    if ( (adx = dx = (X2 - X1) >> 4) < 0 )
    {
        adx = -adx;
    }
    if ( (ady = dy = (Y2 - Y1) >> 4) < 0 )
    {
        ady = -ady;
    }

    if ( adx > ady )
    {
        //
        // X Major line
        //
        gdx  = (dx > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);

        if (ady == 0)
        {
            //
            // Horizontal lines
            //
            gdy = 0;
        }
        else
        {
            //
            // We dont necessarily want to push any lines through Permedia2 that
            // might not be conformant
            //
            if ( (adx > MAX_LENGTH_CONFORMANT_INTEGER_LINES)
               &&(permediaInfo->flags & GLICAP_NT_CONFORMANT_LINES) )
            {
                return(FALSE);
            }
            gdy = INTtoFIXED(dy); 

            //
            // Need to explicitly round delta down for -ve deltas.
            //
            if ( dy < 0 )
            {
                gdy -= adx - 1;
            }

            gdy /= adx;
        }
        count = adx;
    }// if ( adx > ady )
    else if (adx < ady)
    {
        //
        // Y Major line
        //
        gdy = (dy > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);

        if ( adx == 0 )
        {
            //
            // Vertical lines
            //
            gdx = 0;
        }
        else
        {
            //
            // We dont necessarily want to push any lines through Permedia2 that
            // might not be conformant
            //
            if ( (ady > MAX_LENGTH_CONFORMANT_INTEGER_LINES)
               &&(permediaInfo->flags & GLICAP_NT_CONFORMANT_LINES) )
            {
                return(FALSE);
            }

            gdx = INTtoFIXED(dx); 

            //
            // Need to explicitly round delta down for -ve deltas.
            //
            if ( dx < 0 )
            {
                gdx -= ady - 1;
            }

            gdx /= ady; 
        }
        count = ady;
    }
    else
    {
        //
        // Special case for 45 degree lines. These are always conformant.
        //
        if ( ady == 0 )
        {
            return(TRUE); // adx == ady == 0! Nothing to draw.
        }

        gdx  = (dx > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);
        gdy  = (dy > 0) ? INTtoFIXED(1) : INTtoFIXED(-1);
        count = adx;        
    }

    InputBufferReserve(ppdev, 10 , &pBuffer);
    
    //
    // Set up the start point
    //
    DBG_GDI((7, "Loading dXDom 0x%x, dY 0x%x, count 0x%x", gdx, gdy, count));
    
    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  gdx;
    pBuffer[2] = __Permedia2TagdY;
    pBuffer[3] =  gdy;
    pBuffer[4] = __Permedia2TagContinueNewLine;
    pBuffer[5] =  count;

    //
    // Restore dXDom and dY to their defaults
    //
    pBuffer[6] = __Permedia2TagdXDom;
    pBuffer[7] =  0;
    pBuffer[8] = __Permedia2TagdY;
    pBuffer[9] =  INTtoFIXED(1);

    pBuffer += 10;

    InputBufferCommit(ppdev, pBuffer);

    DBG_GDI((STRIP_LOG_LEVEL + 1, "bFastIntegerContinueLine Done"));

    return(TRUE);
}// bFastIntegerContinueLine()

//-----------------------------------------------------------------------------
//
//  VOID vSolidHorizontal
// 
//  Draws left-to-right x-major near-horizontal lines using short-stroke
//  vectors.  
// 
//-----------------------------------------------------------------------------
VOID
vSolidHorizontalLine(PDev*       ppdev,
                     STRIP*      pStrip,
                     LINESTATE*  pLineState)
{
    LONG    cStrips;
    PLONG   pStrips;
    LONG    iCurrent;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "vSolidHorizontalLine"));

    cStrips = pStrip->cStrips;

    InputBufferReserve(ppdev, 16, &pBuffer);

    //
    // Set up the start point
    //
    pBuffer[0] = __Permedia2TagStartXDom;
    pBuffer[1] =  INTtoFIXED(pStrip->ptlStart.x);
    pBuffer[2] = __Permedia2TagStartY;
    pBuffer[3] =  INTtoFIXED(pStrip->ptlStart.y);

    //
    // Set up the deltas for rectangle drawing. Also set Y return value.
    //
    if ( !(pStrip->flFlips & FL_FLIP_V) )
    {

        pBuffer[4] = __Permedia2TagdXDom;
        pBuffer[5] =  INTtoFIXED(0);
        pBuffer[6] = __Permedia2TagdXSub;
        pBuffer[7] =  INTtoFIXED(0);
        pBuffer[8] = __Permedia2TagdY;
        pBuffer[9] =  INTtoFIXED(1);

        pStrip->ptlStart.y += cStrips;
    }
    else
    {
        pBuffer[4] = __Permedia2TagdXDom;
        pBuffer[5] =  INTtoFIXED(0);
        pBuffer[6] = __Permedia2TagdXSub;
        pBuffer[7] =  INTtoFIXED(0);
        pBuffer[8] = __Permedia2TagdY;
        pBuffer[9] =  INTtoFIXED(-1);

        pStrip->ptlStart.y -= cStrips;
    }

    pStrips = pStrip->alStrips;

    //
    // We have to do first strip manually, as we have to use RENDER
    // for the first strip, and CONTINUENEW... for the following strips
    //
    iCurrent = pStrip->ptlStart.x + *pStrips++;     // Xsub, Start of next strip
    
    pBuffer[10] = __Permedia2TagStartXSub;
    pBuffer[11] =  INTtoFIXED(iCurrent);
    pBuffer[12] = __Permedia2TagCount;
    pBuffer[13] =  1;                   // Rectangle 1 scanline high
    pBuffer[14] = __Permedia2TagRender;
    pBuffer[15] =  __RENDER_TRAPEZOID_PRIMITIVE;

    pBuffer += 16;

    InputBufferCommit(ppdev, pBuffer);

    if ( --cStrips )
    {
        while ( cStrips > 1 )
        {
            //
            // First strip of each pair to fill. XSub is valid. Need new Xdom
            //
            iCurrent += *pStrips++;
            
            InputBufferReserve(ppdev, 8, &pBuffer);
            pBuffer[0] = __Permedia2TagStartXDom;
            pBuffer[1] =  INTtoFIXED(iCurrent);
            pBuffer[2] = __Permedia2TagContinueNewDom;
            pBuffer[3] =  1;

            //
            // Second strip of each pair to fill. XDom is valid. Need new XSub
            //
            iCurrent += *pStrips++;
            pBuffer[4] = __Permedia2TagStartXSub;
            pBuffer[5] =  INTtoFIXED(iCurrent);
            pBuffer[6] = __Permedia2TagContinueNewSub;
            pBuffer[7] =  1;

            pBuffer += 8;

            InputBufferCommit(ppdev, pBuffer);

            cStrips -=2;
        }// while ( cStrips > 1 )

        //
        // We may have one last line to draw. Xsub will be valid.
        //
        if ( cStrips )
        {
            iCurrent += *pStrips++;

            InputBufferReserve(ppdev, 4, &pBuffer);

            pBuffer[0] = __Permedia2TagStartXDom;
            pBuffer[1] =  INTtoFIXED(iCurrent);
            pBuffer[2] = __Permedia2TagContinueNewDom;
            pBuffer[3] =  1;

            pBuffer += 4;

            InputBufferCommit(ppdev, pBuffer);

        }
    }// if ( --cStrips )

    //
    // Return last point. Y already calculated when we knew the direction.
    //
    pStrip->ptlStart.x = iCurrent;

    if ( pStrip->flFlips & FL_FLIP_V )
    {
        //
        // Restore hardware to default state
        //
        InputBufferReserve(ppdev, 2, &pBuffer);

        pBuffer[0] = __Permedia2TagdY;
        pBuffer[1] =  INTtoFIXED(1);

        pBuffer += 2;

        InputBufferCommit(ppdev, pBuffer);
    }

}// vSolidHorizontalLine()

//-----------------------------------------------------------------------------
//
//  VOID vSolidVertical
// 
//  Draws left-to-right y-major near-vertical lines using short-stroke
//  vectors.  
// 
//-----------------------------------------------------------------------------
VOID
vSolidVerticalLine(PDev*       ppdev,
                   STRIP*      pStrip,
                   LINESTATE*  pLineState)
{
    LONG    cStrips, yDir;
    PLONG   pStrips;
    LONG    iCurrent, iLen, iLenSum;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "vSolidVerticalLine"));

    cStrips = pStrip->cStrips;

    InputBufferReserve(ppdev, 16, &pBuffer);
    
    //
    // Set up the start point
    //
    pBuffer[0] = __Permedia2TagStartXDom;
    pBuffer[1] =  INTtoFIXED(pStrip->ptlStart.x);
    pBuffer[2] = __Permedia2TagStartY;
    pBuffer[3] =  INTtoFIXED(pStrip->ptlStart.y);
    pBuffer[4] = __Permedia2TagdXDom;
    pBuffer[5] =  INTtoFIXED(0);
    pBuffer[6] = __Permedia2TagdXSub;
    pBuffer[7] =  INTtoFIXED(0);

    //
    // Set up the deltas for rectangle drawing.
    // dxDom, dXSub and dY all are to 0, 0, and 1 by default 
    //
    if ( !(pStrip->flFlips & FL_FLIP_V) )
    {
        yDir = 1;
    }
    else
    {
        yDir = -1;
    }
    pBuffer[8] = __Permedia2TagdY;
    pBuffer[9] =  INTtoFIXED(yDir);

    pStrips = pStrip->alStrips;

    //
    // We have to do first strip manually, as we have to use RENDER
    // for the first strip, and CONTINUENEW... for the following strips
    //
    iCurrent = pStrip->ptlStart.x + 1;          // Xsub, Start of next strip
    iLenSum = (iLen = *pStrips++);
    pBuffer[10] = __Permedia2TagStartXSub;
    pBuffer[11] =  INTtoFIXED(iCurrent);
    pBuffer[12] = __Permedia2TagCount;
    pBuffer[13] =  iLen;           // Rectangle 1 scanline high
    pBuffer[14] = __Permedia2TagRender;
    pBuffer[15] =  __RENDER_TRAPEZOID_PRIMITIVE;

    pBuffer += 16;

    InputBufferCommit(ppdev, pBuffer);

    if ( --cStrips )
    {
        while ( cStrips > 1 )
        {
            //
            // First strip of each pair to fill. XSub is valid. Need new Xdom
            //
            iCurrent++;

            InputBufferReserve(ppdev, 8, &pBuffer);
            
            pBuffer[0] = __Permedia2TagStartXDom;
            pBuffer[1] =  INTtoFIXED(iCurrent);

            iLenSum += (iLen = *pStrips++);
            pBuffer[2] = __Permedia2TagContinueNewDom;
            pBuffer[3] =  iLen;

            //
            // Second strip of each pair to fill. XDom is valid. Need new XSub
            //
            iCurrent ++;
            pBuffer[4] = __Permedia2TagStartXSub;
            pBuffer[5] =  INTtoFIXED(iCurrent);
            iLenSum += (iLen = *pStrips++);
            pBuffer[6] = __Permedia2TagContinueNewSub;
            pBuffer[7] =  iLen;

            pBuffer += 8;

            InputBufferCommit(ppdev, pBuffer);

            cStrips -=2;
        }// while ( cStrips > 1 )

        //
        // We may have one last line to draw. Xsub will be valid.
        //
        if ( cStrips )
        {
            iCurrent ++;
            InputBufferReserve(ppdev, 4, &pBuffer);

            pBuffer[0] = __Permedia2TagStartXDom;
            pBuffer[1] =  INTtoFIXED(iCurrent);
            
            iLenSum += (iLen = *pStrips++);
            pBuffer[2] = __Permedia2TagContinueNewDom;
            pBuffer[3] =  iLen;

            pBuffer += 4;

            InputBufferCommit(ppdev, pBuffer);
        }
    }// if ( --cStrips )

    //
    // Restore hardware to default
    //
    InputBufferReserve(ppdev, 2, &pBuffer);
    
    pBuffer[0] = __Permedia2TagdY;
    pBuffer[1] =  INTtoFIXED(1);
    
    pBuffer += 2;
    
    InputBufferCommit(ppdev, pBuffer);


    //
    // Return last point. 
    //
    pStrip->ptlStart.x = iCurrent;
    pStrip->ptlStart.y += iLenSum * yDir;
    
    DBG_GDI((STRIP_LOG_LEVEL + 1, "vSolidVerticalLine done"));

}// vSolidVerticalLine()

//-----------------------------------------------------------------------------
//
//  VOID vSolidDiagonalVertical
// 
//  Draws left-to-right y-major near-diagonal lines using short-stroke
//  vectors.  
// 
//-----------------------------------------------------------------------------
VOID
vSolidDiagonalVerticalLine(PDev*       ppdev,
                           STRIP*      pStrip,
                           LINESTATE*  pLineState)
{
    LONG    cStrips, yDir;
    PLONG   pStrips;
    LONG    iCurrent, iLen, iLenSum;
    ULONG*  pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "vSolidDiagonalVerticalLine"));

    cStrips = pStrip->cStrips;

    if ( !(pStrip->flFlips & FL_FLIP_V) )
    {
        yDir = 1;
    }
    else
    {
        yDir = -1;
    }

    InputBufferReserve(ppdev, 16, &pBuffer);

    //
    // Set up the deltas for rectangle drawing.
    //
    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  INTtoFIXED(1);
    pBuffer[2] = __Permedia2TagdXSub;
    pBuffer[3] =  INTtoFIXED(1);
    pBuffer[4] = __Permedia2TagdY;
    pBuffer[5] =  INTtoFIXED(yDir);

    pStrips = pStrip->alStrips;

    //
    // We have to do first strip manually, as we have to use RENDER
    // for the first strip, and CONTINUENEW... for the following strips
    //
    pBuffer[6] = __Permedia2TagStartY;
    pBuffer[7] =  INTtoFIXED(pStrip->ptlStart.y);
    pBuffer[8] = __Permedia2TagStartXDom;
    pBuffer[9] =  INTtoFIXED(pStrip->ptlStart.x + 1);
    pBuffer[10] = __Permedia2TagStartXSub;
    pBuffer[11] =  INTtoFIXED(pStrip->ptlStart.x);

    iLenSum = (iLen = *pStrips++);
    iCurrent = pStrip->ptlStart.x + iLen - 1;// Start of next strip

    pBuffer[12] = __Permedia2TagCount;
    pBuffer[13] =  iLen;           // Trap iLen scanline high
    pBuffer[14] = __Permedia2TagRender;
    pBuffer[15] =  __RENDER_TRAPEZOID_PRIMITIVE;

    pBuffer += 16;

    InputBufferCommit(ppdev, pBuffer);

    if ( --cStrips )
    {
        while ( cStrips > 1 )
        {
            //
            // First strip of each pair to fill. XSub is valid. Need new Xdom
            //
            InputBufferReserve(ppdev, 8, &pBuffer);

            pBuffer[0] = __Permedia2TagStartXDom;
            pBuffer[1] =  INTtoFIXED(iCurrent);
            iLenSum += (iLen = *pStrips++);
            iCurrent += iLen - 1;
            pBuffer[2] = __Permedia2TagContinueNewDom;
            pBuffer[3] =  iLen;

            //
            // Second strip of each pair to fill. XDom is valid. Need new XSub
            //
            pBuffer[4] = __Permedia2TagStartXSub;
            pBuffer[5] =  INTtoFIXED(iCurrent);
            iLenSum += (iLen = *pStrips++);
            iCurrent += iLen - 1;
            pBuffer[6] = __Permedia2TagContinueNewSub;
            pBuffer[7] =  iLen;

            pBuffer += 8;

            InputBufferCommit(ppdev, pBuffer);

            cStrips -=2;
        }// while ( cStrips > 1 )

        //
        // We may have one last line to draw. Xsub will be valid.
        //
        if ( cStrips )
        {
            InputBufferReserve(ppdev, 4, &pBuffer);
            pBuffer[0] = __Permedia2TagStartXDom;
            pBuffer[1] =  INTtoFIXED(iCurrent);
            iLenSum += (iLen = *pStrips++);
            iCurrent += iLen - 1;
            pBuffer[2] = __Permedia2TagContinueNewDom;
            pBuffer[3] =  iLen;

            pBuffer += 4;

            InputBufferCommit(ppdev, pBuffer);
        }
    }// if ( --cStrips )

    InputBufferReserve(ppdev, 6, &pBuffer);

    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  0;
    pBuffer[2] = __Permedia2TagdXSub;
    pBuffer[3] =  0;
    pBuffer[4] = __Permedia2TagdY;
    pBuffer[5] =  INTtoFIXED(1);

    pBuffer += 6;

    InputBufferCommit(ppdev, pBuffer);

    //
    // Return last point. 
    //
    pStrip->ptlStart.x = iCurrent;
    pStrip->ptlStart.y += iLenSum * yDir;
    
    DBG_GDI((STRIP_LOG_LEVEL + 1, "vSolidDiagonalVerticalLine done"));

}// vSolidDiagonalVerticalLine()

//-----------------------------------------------------------------------------
//
//  VOID vSolidDiagonalHorizontalLine
// 
//  Draws left-to-right x-major near-diagonal lines using short-stroke
//  vectors.  
// 
//-----------------------------------------------------------------------------
VOID
vSolidDiagonalHorizontalLine(PDev*       ppdev,
                             STRIP*      pStrip,
                             LINESTATE*  pLineState)
{
    LONG    cStrips, yDir, xCurrent, yCurrent, iLen;
    PLONG   pStrips;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "vSolidDiagonalHorizontalLine"));

    // This routine has to be implemented in a different way to the other 3
    // solid line drawing functions because the rasterizer unit will not 
    // produce 2 pixels on the same scanline without a lot of effort in 
    // producing delta values. In this case, we have to draw a complete new
    // primitive for each strip. Therefore, we have to use lines rather than
    // trapezoids to generate the required strips. With lines we use 4 messages
    // per strip, where trapezoids would use 5.

    cStrips = pStrip->cStrips;

    if ( !(pStrip->flFlips & FL_FLIP_V) )
    {
        yDir = 1;
    }
    else
    {
        yDir = -1;
    }

    pStrips = pStrip->alStrips;

    xCurrent = pStrip->ptlStart.x;
    yCurrent = pStrip->ptlStart.y;


    InputBufferReserve(ppdev, 6, &pBuffer);
    
    //
    // Set up the deltas for rectangle drawing.
    //
    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  INTtoFIXED(1);
    pBuffer[2] = __Permedia2TagdXSub;
    pBuffer[3] =  INTtoFIXED(1);
    pBuffer[4] = __Permedia2TagdY;
    pBuffer[5] =  INTtoFIXED(yDir);

    pBuffer += 6;

    InputBufferCommit(ppdev, pBuffer);

    while ( TRUE )
    {
        //
        // Set up the start point
        //
        InputBufferReserve(ppdev, 8, &pBuffer);

        pBuffer[0] = __Permedia2TagStartXDom;
        pBuffer[1] =  INTtoFIXED(xCurrent);
        pBuffer[2] = __Permedia2TagStartY;
        pBuffer[3] =  INTtoFIXED(yCurrent);

        iLen = *pStrips++;
        pBuffer[4] = __Permedia2TagCount;
        pBuffer[5] =  iLen;
        pBuffer[6] = __Permedia2TagRender;
        pBuffer[7] =  __RENDER_LINE_PRIMITIVE;

        pBuffer += 8;

        InputBufferCommit(ppdev, pBuffer);

        xCurrent += iLen;
        if ( yDir > 0 )
        {
            yCurrent += iLen - 1;
        }
        else
        {
            yCurrent -= iLen - 1;
        }

        if ( !(--cStrips) )
        {
            break;
        }
    }// while ( TRUE )

    InputBufferReserve(ppdev, 6, &pBuffer);
    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  0;
    pBuffer[2] = __Permedia2TagdXSub;
    pBuffer[3] =  0;
    pBuffer[4] = __Permedia2TagdY;
    pBuffer[5] =  INTtoFIXED(1);

    pBuffer += 6;

    InputBufferCommit(ppdev, pBuffer);

    //
    // Return last point. 
    //
    pStrip->ptlStart.x = xCurrent;
    pStrip->ptlStart.y = yCurrent;
    
    DBG_GDI((STRIP_LOG_LEVEL + 1, "vSolidDiagonalHorizontalLine done"));

}// vSolidDiagonalHorizontalLine()

//-----------------------------------------------------------------------------
//
// VOID vStyledHorizontalLine()
// 
// Takes the list of strips that define the pixels that would be lit for
// a solid line, and breaks them into styling chunks according to the
// styling information that is passed in.
// 
// This particular routine handles x-major lines that run left-to-right,
// and are comprised of horizontal strips.  It draws the dashes using
// short-stroke vectors.
// 
// The performance of this routine could be improved significantly.
//
// Parameters
//  ppdev-------PDEV pointer
//  pStrip------Strip info. Note: the data in the strip are already in normal
//              integer format, not 28.4 format
//  pLineState--Line state info
//
//-----------------------------------------------------------------------------
VOID
vStyledHorizontalLine(PDev*       ppdev,
                      STRIP*      pStrip,
                      LINESTATE*  pLineState)
{
    LONG    x;
    LONG    y;
    LONG    dy;
    LONG*   plStrip;
    ULONG*      pBuffer;
    
    LONG    lStripLength;
    LONG    lTotalNumOfStrips;
    
    LONG    lNumPixelRemain;
    LONG    lCurrentLength;
    ULONG   bIsGap;
    
    PERMEDIA_DECL;

    DBG_GDI((STRIP_LOG_LEVEL, "vStyledHorizontalLine"));

    if ( pStrip->flFlips & FL_FLIP_V )
    {
        dy = -1;
    }
    else
    {
        dy = 1;
    }

    lTotalNumOfStrips = pStrip->cStrips;// Total number of strips we'll do
    plStrip = pStrip->alStrips;         // Points to current strip
    x = pStrip->ptlStart.x;             // x position of start of first strip
    y = pStrip->ptlStart.y;             // y position of start of first strip

    //
    // Set up the deltas for horizontal line drawing.
    //
    InputBufferReserve(ppdev, 4, &pBuffer);

    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  INTtoFIXED(1);
    pBuffer[2] = __Permedia2TagdY;
    pBuffer[3] =  0;
    
    pBuffer += 4;

    InputBufferCommit(ppdev, pBuffer);

    lStripLength = *plStrip;            // Number of pixels in first strip

    //
    // Number of pixels in first strip
    //
    lNumPixelRemain = pLineState->spRemaining;

    //
    // ulStyleMask is non-zero if we're in the middle of a 'gap',
    // and zero if we're in the middle of a 'dash':
    //
    bIsGap = pLineState->ulStyleMask;
    if ( bIsGap )
    {
        //
        // A gap
        //
        goto SkipAGap;
    }
    else
    {
        //
        // A dash
        //
        goto OutputADash;
    }

PrepareToSkipAGap:

    //
    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:
    //
    bIsGap = ~bIsGap;
    pLineState->psp++;
    
    if ( pLineState->psp > pLineState->pspEnd )
    {
        pLineState->psp = pLineState->pspStart;
    }

    lNumPixelRemain = *pLineState->psp;

    //
    // If 'lStripLength' is zero, we also need a new strip:
    //
    if ( lStripLength != 0 )
    {
        goto SkipAGap;
    }

    //
    // Here, we're in the middle of a 'gap' where we don't have to
    // display anything.  We simply cycle through all the strips
    // we can, keeping track of the current position, until we run
    // out of 'gap':
    //
    while ( TRUE )
    {
        //
        // Each time we loop, we move to a new scan and need a new strip
        //
        y += dy;

        plStrip++;
        lTotalNumOfStrips--;
        
        if ( lTotalNumOfStrips == 0 )
        {
            goto AllDone;
        }

        lStripLength = *plStrip;

SkipAGap:

        lCurrentLength = min(lStripLength, lNumPixelRemain);
        lNumPixelRemain -= lCurrentLength;
        lStripLength -= lCurrentLength;

        x += lCurrentLength;

        if ( lNumPixelRemain == 0 )
        {
            goto PrepareToOutputADash;
        }
    }// while (TRUE)

PrepareToOutputADash:

    //
    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:
    //
    bIsGap = ~bIsGap;
    pLineState->psp++;
    
    if ( pLineState->psp > pLineState->pspEnd )
    {
        pLineState->psp = pLineState->pspStart;
    }

    lNumPixelRemain = *pLineState->psp;

    //
    // If 'lStripLength' is zero, we also need a new strip.
    //
    if ( lStripLength != 0 )
    {
        //
        // There's more to be done in the current strip, so set 'y'
        // to be the current scan:
        //
        goto OutputADash;
    }

    while ( TRUE )
    {
        //
        // Each time we loop, we move to a new scan and need a new strip:
        //
        y += dy;

        plStrip++;
        lTotalNumOfStrips--;
        
        if ( lTotalNumOfStrips == 0 )
        {
            goto AllDone;
        }

        lStripLength = *plStrip;

OutputADash:

        lCurrentLength   = min(lStripLength, lNumPixelRemain);
        lNumPixelRemain -= lCurrentLength;
        lStripLength -= lCurrentLength;

        //
        // With Permedia2 we just download the lines to draw
        //
        InputBufferReserve(ppdev, 8, &pBuffer);

        pBuffer[0] = __Permedia2TagStartXDom;
        pBuffer[1] =  INTtoFIXED(x);
        pBuffer[2] = __Permedia2TagStartY;
        pBuffer[3] =  INTtoFIXED(y);
        pBuffer[4] = __Permedia2TagCount;
        pBuffer[5] =  lCurrentLength;
        pBuffer[6] = __Permedia2TagRender;
        pBuffer[7] =  __PERMEDIA_LINE_PRIMITIVE;

        pBuffer += 8;

        InputBufferCommit(ppdev, pBuffer);

        x += lCurrentLength;

        if ( lNumPixelRemain == 0 )
        {
            goto PrepareToSkipAGap;
        }
    }// while ( TRUE )

AllDone:

    //
    // Restore default state
    //
    InputBufferReserve(ppdev, 4, &pBuffer);

    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  0;
    pBuffer[2] = __Permedia2TagdY;
    pBuffer[3] =  INTtoFIXED(1);

    pBuffer += 4;

    InputBufferCommit(ppdev, pBuffer);

    //
    // Update our state variables so that the next line can continue
    // where we left off:
    //
    pLineState->spRemaining   = lNumPixelRemain;
    pLineState->ulStyleMask   = bIsGap;
    pStrip->ptlStart.x = x;
    pStrip->ptlStart.y = y;
    
    DBG_GDI((STRIP_LOG_LEVEL + 1, "vStyledHorizontalLine done"));

}// vStyledHorizontalLine()

//-----------------------------------------------------------------------------
//
//  VOID vStripStyledVertical
// 
//  Takes the list of strips that define the pixels that would be lit for
//  a solid line, and breaks them into styling chunks according to the
//  styling information that is passed in.
// 
//  This particular routine handles y-major lines that run left-to-right,
//  and are comprised of vertical strips.  It draws the dashes using
//  short-stroke vectors.
// 
//  The performance of this routine could be improved significantly.
// 
//-----------------------------------------------------------------------------
VOID
vStyledVerticalLine(PDev*       ppdev,
                    STRIP*      pStrip,
                    LINESTATE*  pLineState)
{
    LONG    x;
    LONG    y;
    LONG    dy;
    LONG*   plStrip;
    LONG    cStrips;
    LONG    cStyle;
    LONG    cStrip;
    LONG    cThis;
    ULONG   bIsGap;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "vStyledVerticalLine"));
    if ( pStrip->flFlips & FL_FLIP_V )
    {
        dy = -1;
    }
    else
    {
        dy = 1;
    }

    cStrips = pStrip->cStrips;      // Total number of strips we'll do
    plStrip = pStrip->alStrips;     // Points to current strip
    x       = pStrip->ptlStart.x;   // x position of start of first strip
    y       = pStrip->ptlStart.y;   // y position of start of first strip

    //
    // Set up the deltas for vertical line drawing.
    //
    InputBufferReserve(ppdev, 6, &pBuffer);
    
    pBuffer[0] = __Permedia2TagdXDom;
    pBuffer[1] =  INTtoFIXED(0);
    pBuffer[2] = __Permedia2TagdXSub;
    pBuffer[3] =  INTtoFIXED(0);
    pBuffer[4] = __Permedia2TagdY;
    pBuffer[5] =  INTtoFIXED(dy);

    pBuffer += 6;

    InputBufferCommit(ppdev, pBuffer);

    cStrip = *plStrip;              // Number of pels in first strip

    cStyle = pLineState->spRemaining;      // Number of pels in first 'gap' or 'dash'
    bIsGap = pLineState->ulStyleMask;      // Tells whether in a 'gap' or a 'dash'

    // ulStyleMask is non-zero if we're in the middle of a 'gap',
    // and zero if we're in the middle of a 'dash':

    if ( bIsGap )
    {
        goto SkipAGap;
    }
    else
    {
        goto OutputADash;
    }

PrepareToSkipAGap:

    //
    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:
    //
    bIsGap = ~bIsGap;
    pLineState->psp++;
    
    if ( pLineState->psp > pLineState->pspEnd )
    {
        pLineState->psp = pLineState->pspStart;
    }

    cStyle = *pLineState->psp;

    //
    // If 'cStrip' is zero, we also need a new strip:
    //
    if ( cStrip != 0 )
    {
        goto SkipAGap;
    }

    //
    // Here, we're in the middle of a 'gap' where we don't have to
    // display anything.  We simply cycle through all the strips
    // we can, keeping track of the current position, until we run
    // out of 'gap':
    //
    while ( TRUE )
    {
        //
        // Each time we loop, we move to a new column and need a new strip:
        //
        x++;

        plStrip++;
        cStrips--;
        
        if ( cStrips == 0 )
        {
            goto AllDone;
        }

        cStrip = *plStrip;

SkipAGap:

        cThis   = min(cStrip, cStyle);
        cStyle -= cThis;
        cStrip -= cThis;

        y += (dy > 0) ? cThis : -cThis;

        if ( cStyle == 0 )
        {
            goto PrepareToOutputADash;
        }
    }

PrepareToOutputADash:

    //
    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:
    //
    bIsGap = ~bIsGap;
    pLineState->psp++;
    
    if ( pLineState->psp > pLineState->pspEnd )
    {
        pLineState->psp = pLineState->pspStart;
    }

    cStyle = *pLineState->psp;

    //
    // If 'cStrip' is zero, we also need a new strip.
    //
    if ( cStrip != 0 )
    {
        goto OutputADash;
    }

    while ( TRUE )
    {
        //
        // Each time we loop, we move to a new column and need a new strip:
        //
        x++;

        plStrip++;
        cStrips--;
        
        if ( cStrips == 0 )
        {
            goto AllDone;
        }

        cStrip = *plStrip;

OutputADash:

        cThis   = min(cStrip, cStyle);
        cStyle -= cThis;
        cStrip -= cThis;

        //
        // With Permedia2 we just download the lines to draw
        //
        InputBufferReserve(ppdev, 8, &pBuffer);

        pBuffer[0] = __Permedia2TagStartXDom;
        pBuffer[1] =  INTtoFIXED(x);
        pBuffer[2] = __Permedia2TagStartY;
        pBuffer[3] =  INTtoFIXED(y);
        pBuffer[4] = __Permedia2TagCount;
        pBuffer[5] =  cThis;
        pBuffer[6] = __Permedia2TagRender;
        pBuffer[7] =  __PERMEDIA_LINE_PRIMITIVE;

        pBuffer += 8;

        InputBufferCommit(ppdev, pBuffer);

        y += (dy > 0) ? cThis : -cThis;

        if ( cStyle == 0 )
        {
            goto PrepareToSkipAGap;
        }
    }// while ( TRUE )

AllDone:
    //
    // Restore hardware to default state
    //
    InputBufferReserve(ppdev, 2, &pBuffer);
    
    pBuffer[0] = __Permedia2TagdY;
    pBuffer[1] =  INTtoFIXED(1);
    
    pBuffer += 2;

    InputBufferCommit(ppdev, pBuffer);

    //
    // Update our state variables so that the next line can continue
    // where we left off:
    //
    pLineState->spRemaining   = cStyle;
    pLineState->ulStyleMask   = bIsGap;
    pStrip->ptlStart.x = x;
    pStrip->ptlStart.y = y;

}// vStyledVerticalLine()

//
// For a given sub-pixel coordinate (x.m, y.n) in 28.4 fixed point
// format this array is indexed by (m,n) and indicates whether the
// given sub-pixel is within a GIQ diamond. m coordinates run left
// to right; n coordinates ru top to bottom so index the array with
// ((n<<4)+m). The array as seen here really contains 4 quarter
// diamonds.
//
static unsigned char    in_diamond[] =
{
/*          0 1 2 3 4 5 6 7 8 9 a b c d e f          */

/* 0 */     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 0 */
/* 1 */     1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,    /* 1 */
/* 2 */     1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,    /* 2 */
/* 3 */     1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,    /* 3 */
/* 4 */     1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,    /* 4 */
/* 5 */     1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,    /* 5 */
/* 6 */     1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,    /* 6 */
/* 7 */     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 7 */
/* 8 */     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 8 */
/* 9 */     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 9 */
/* a */     1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,    /* a */
/* b */     1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,    /* b */
/* c */     1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,    /* c */
/* d */     1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,    /* d */
/* e */     1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,    /* e */
/* f */     1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,    /* f */

/*      0 1 2 3 4 5 6 7 8 9 a b c d e f          */
};

//
// For lines with abs(slope) != 1 use IN_DIAMOND to determine if an
// end point is in a diamond. For lines of slope = 1 use IN_S1DIAMOND.
// For lines of slope = -1 use IN_SM1DIAMOND. The last two are a bit
// strange. The documentation leaves us with a problem for slope 1
// lines which run exactly betwen the diamonds. According to the docs
// such a line can enter a diamond, leave it and enter again. This is
// plainly rubbish so along the appropriate edge of the diamond we
// consider a slope 1 line to be inside the diamond. This is the
// bottom right edge for lines of slope -1 and the bottom left edge for
// lines of slope 1.
//
#define IN_DIAMOND(m, n)    (in_diamond[((m) << 4) + (n)])
#define IN_S1DIAMOND(m, n)  ((in_diamond[((m) << 4) + (n)]) || \
                        ((m) - (n) == 8))
#define IN_SM1DIAMOND(m, n) ((in_diamond[((m) << 4) + (n)]) || \
                        ((m) + (n) == 8))

BOOL
bFastLine(PPDev     ppdev,
          LONG      fx1,
          LONG      fy1,
          LONG      fx2,
          LONG      fy2)

{
    register LONG   adx, ady, tmp;
    FIX         m1, n1, m2, n2;
    LONG    dx, dy;
    LONG    dX, dY;
    LONG    count, startX, startY;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;
    
    DBG_GDI((STRIP_LOG_LEVEL, "bFastLine"));

    //
    // This function is only called if we have a line with non integer end points
    // and the unsigned coordinates are no greater than 15.4. 
    //
    // We can only guarantee to do lines whose coords need <= 12 bits
    // of integer. This is because to get the delta we must shift
    // by 16 bits. This includes 4 bits of fraction which means if
    // we have more than 12 bits of integer we get overrun on the
    // shift. We could use floating point to give us a better 16
    // bits of integer but this requires an extra set of multiplies
    // and divides in order to convert from 28.4 to fp. In any case
    // we have to have a test to reject coords needing > 16 bits
    // of integer.
    // Actually, we can deal with 16.4 coordinates provided dx and dy
    // never require more than 12 bits of integer.
    // So optimise for the common case where the line is completely
    // on the screen (actually 0 to 2047.f). Since the coords have
    // 4 bits of fraction we note that a 32 bit signed number
    // outside the range 0 to 2047.f will have one of its top 17
    // bits set. So logical or all the coords and test against
    // 0xffff8000. This is about as quick a test as we can get for
    // both ends of the line being on the screen. If this test fails
    // then we can check everything else at a leisurely pace.
    //

    //
    // Get signed and absolute deltas
    //
    if ((adx = dx = fx2 - fx1) < 0)
    {
        adx = -adx;
    }
    if ((ady = dy = fy2 - fy1) < 0)
    {
        ady = -ady;
    }

    //
    // Refuse to draw any lines whose delta is out of range.
    // We have to shift the delta by 16, so we dont want to loose any precision
    //
    if ( (adx | ady) & 0xffff8000 )
    {
        return(FALSE);
    }

    //
    // Fractional bits are used to check if point is in a diamond
    //
    m1 = fx1 & 0xf;
    n1 = fy1 & 0xf;
    m2 = fx2 & 0xf;
    n2 = fy2 & 0xf;

    //
    // The rest of the code is a series of cases. Each one is "called" by a
    // goto. This is simply to keep the nesting down. Main cases are: lines
    // with absolute slope == 1; x-major lines; and y-major lines. We draw
    // lines as they are given rather than always drawing in one direction.
    // This adds extra code but saves the time required to swap the points
    // and adjust for not drawing the end point.
    //
    startX = fx1 << 12;
    startY = fy1 << 12;

    DBG_GDI((7, "GDI Line %x, %x  deltas %x, %x", startX, startY, dx, dy));

    if ( adx < ady )
    {
        goto y_major;
    }

    if ( adx > ady )
    {
        goto x_major;
    }

    //
    // All slope 1 lines are sampled in X. i.e. we move the start coord to
    // an integer x and let Permedia2 truncate in y. This is because all GIQ
    // lines are rounded down in y for values exactly half way between two
    // pixels. If we sampled in y then we would have to round up in x for
    // lines of slope 1 and round down in x for other lines. Sampling in x
    // allows us to use the same Permedia2 bias in all cases (0x7fff). We do
    // the x round up or down when we move the start point.
    //
    if ( dx != dy )
    {
        goto slope_minus_1;
    }
    if ( dx < 0 )
    {
        goto slope1_reverse;
    }

    dX = 1 << 16;
    dY = 1 << 16;

    if ( IN_S1DIAMOND(m1, n1) )
    {
        tmp = (startX + 0x8000) & ~0xffff;
    }
    else
    {
        tmp = (startX + 0xffff) & ~0xffff;
    }
    startY += tmp - startX;
    startX = tmp;
    
    if ( IN_S1DIAMOND(m2, n2) )
    {
        fx2 = (fx2 + 0x8) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 = (fx2 + 0xf) & ~0xf;   // next integer
    }
    count = (fx2 >> 4) - (startX >> 16);

    goto Draw_Line;

    slope1_reverse:
    dX = -1 << 16;
    dY = -1 << 16;

    if ( IN_S1DIAMOND(m1, n1) )
    {
        tmp = (startX + 0x8000) & ~0xffff;
    }
    else
    {
        tmp = startX & ~0xffff;
    }
    startY += tmp - startX;
    startX = tmp;
    
    if ( IN_S1DIAMOND(m2, n2) )
    {
        fx2 = (fx2 + 0x8) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 &= ~0xf;                // previous integer
    }

    count = (startX >> 16) - (fx2 >> 4);

    goto Draw_Line;

    slope_minus_1:
    
    if ( dx < 0 )
    {
        goto slope_minus_dx;
    }

    //
    // dx > 0, dy < 0
    //
    dX = 1 << 16;
    dY = -1 << 16;
    
    if (IN_SM1DIAMOND(m1, n1))
    {
        tmp = (startX + 0x7fff) & ~0xffff;
    }
    else
    {
        tmp = (startX + 0xffff) & ~0xffff;
    }
    
    startY += startX - tmp;
    startX = tmp;
    
    if (IN_SM1DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 = (fx2 + 0xf) & ~0xf;   // next integer
    }
    count = (fx2 >> 4) - (startX >> 16);

    goto Draw_Line;

    slope_minus_dx:
    dX = -1 << 16;
    dY = 1 << 16;
    
    if ( IN_SM1DIAMOND(m1, n1) )
    {
        tmp = (startX + 0x7fff) & ~0xffff;
    }
    else
    {
        tmp = startX & ~0xffff;
    }
    startY += startX - tmp;
    startX = tmp;
    
    if ( IN_SM1DIAMOND(m2, n2) )
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 &= ~0xf;                // previous integer
    }
    count = (startX >> 16) - (fx2 >> 4);

    goto Draw_Line;

    x_major:
    
    //
    // Dont necessarily render through Permedia2 if we are worried about
    // conformance.
    //
    if ( (adx > (MAX_LENGTH_CONFORMANT_NONINTEGER_LINES << 4))
       &&(permediaInfo->flags & GLICAP_NT_CONFORMANT_LINES)
       &&(ady != 0) )
    {
        return(FALSE);
    }

    if ( dx < 0 )
    {
        goto right_to_left_x;
    }

    //
    // Line goes left to right. Round up the start x to an integer
    // coordinate. This is the coord of the first diamond that the
    // line crosses. Adjust start y to match this point on the line.
    //
    dX = 1 << 16;
    if ( IN_DIAMOND(m1, n1) )
    {
        tmp = (startX + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = (startX + 0xffff) & ~0xffff;  // next integer
    }

    //
    // We can optimise for horizontal lines
    //
    if ( dy != 0 )
    {
        dY = dy << 16;

        //
        // Need to explicitly round delta down for -ve deltas.
        //
        if ( dy < 0 )
        {
            dY -= adx - 1;
        }

        dY /= adx;
        startY += (((tmp - startX) >> 12) * dY) >> 4;
    }
    else
    {
        dY = 0;
    }
    startX = tmp;

    if ( IN_DIAMOND(m2, n2) )
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 = (fx2 + 0xf) & ~0xf;   // next integer
    }

    count = (fx2 >> 4) - (startX >> 16);

    goto Draw_Line;

    right_to_left_x:

    dX = -1 << 16;
    if ( IN_DIAMOND(m1, n1) )
    {
        tmp = (startX + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = startX & ~0xffff;             // previous integer
    }

    //
    // We can optimise for horizontal lines
    //
    if (dy != 0)
    {
        dY = dy << 16;

        //
        // Need to explicitly round delta down for -ve deltas.
        //
        if ( dy < 0 )
        {
            dY -= adx - 1;
        }

        dY /= adx;
        startY += (((startX - tmp) >> 12) * dY) >> 4;
    }
    else
    {
        dY = 0;
    }
    startX = tmp;

    if ( IN_DIAMOND(m2, n2) )
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 &= ~0xf;                // previous integer
    }
    count = (startX >> 16) - (fx2 >> 4);

    goto Draw_Line;

y_major:
    //
    // Dont necessarily render through Permedia2 if we are worried
    // about conformance.
    //
    if ( (ady > (MAX_LENGTH_CONFORMANT_NONINTEGER_LINES << 4))
       &&(permediaInfo->flags & GLICAP_NT_CONFORMANT_LINES)
       &&(adx != 0) )
    {
        return(FALSE);
    }

    if ( dy < 0 )
    {
        goto high_to_low_y;
    }
    
    dY = 1 << 16;
    if ( IN_DIAMOND(m1, n1) )
    {
        tmp = (startY + 0x7fff) & ~0xffff;      // nearest integer
    }
    else
    {
        tmp = (startY + 0xffff) & ~0xffff;      // next integer
    }

    //
    // We can optimise for vertical lines
    //
    if ( dx != 0 )
    {
        dX = dx << 16;

        //
        // Need to explicitly round delta down for -ve deltas.
        //
        if ( dx < 0 )
        {
            dX -= ady - 1;
        }

        dX /= ady;
        startX += (((tmp - startY) >> 12) * dX) >> 4;
    }
    else
    {
        dX = 0;
    }
    startY = tmp;

    if ( IN_DIAMOND(m2, n2) )
    {
        fy2 = (fy2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fy2 = (fy2 + 0xf) & ~0xf;   // next integer
    }
    count = (fy2 >> 4) - (startY >> 16);

    goto Draw_Line;

    high_to_low_y:

    dY = -1 << 16;
    if ( IN_DIAMOND(m1, n1) )
    {
        tmp = (startY + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = startY & ~0xffff;             // previous integer
    }

    //
    // We can optimise for horizontal lines
    //
    if ( dx != 0 )
    {
        dX = dx << 16;

        //
        // Need to explicitly round delta down for -ve deltas.
        //
        if ( dx < 0 )
        {
            dX -= ady - 1;
        }

        dX /= ady;
        startX += (((startY - tmp) >> 12) * dX) >> 4;
    }
    else
    {
        dX = 0;
    }
    startY = tmp;

    if ( IN_DIAMOND(m2, n2) )
    {
        fy2 = (fy2 + 0x7) & ~0xf;       // nearest integer
    }
    else
    {
        fy2 &= ~0xf;                    // previous integer
    }
    count = (startY >> 16) - (fy2 >> 4);

Draw_Line:
    //
    // We need 6 fifo entries to draw a line
    //
    InputBufferReserve(ppdev, 16, &pBuffer);

    DBG_GDI((7, "Line %x, %x  deltas %x, %x  Count %x",
             startX + 0x7fff, startY + 0x7fff, dX, dY, count));

    pBuffer[0] = __Permedia2TagStartXDom;
    pBuffer[1] =   startX + 0x7fff;
    pBuffer[2] = __Permedia2TagStartY;
    pBuffer[3] =      startY + 0x7fff;
    pBuffer[4] = __Permedia2TagdXDom;
    pBuffer[5] =       dX;
    pBuffer[6] = __Permedia2TagdY;
    pBuffer[7] =          dY;
    pBuffer[8] = __Permedia2TagCount;
    pBuffer[9] =       count;
    pBuffer[10] = __Permedia2TagRender;
    pBuffer[11] =      __RENDER_LINE_PRIMITIVE;

    //
    // Restore default state
    //
    pBuffer[12] = __Permedia2TagdXDom;
    pBuffer[13] =       0;
    pBuffer[14] = __Permedia2TagdY;
    pBuffer[15] =          INTtoFIXED(1);

    pBuffer += 16;

    InputBufferCommit(ppdev, pBuffer);

    return(TRUE);
}// bFastLine()


