/******************************Module*Header*******************************\
* Module Name: Lineto.c
*
* DrvLineTo for the driver.
*
* Copyright (c) 1995-1996 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"

// The MGA's hardware coordinates are limited to 16-bit signed values:

#define MIN_INTEGER_BOUND (-32767)
#define MAX_INTEGER_BOUND (32767)

/**************************************************************************
* Line Rasterization
*
* The 3D DDI's line rasterization rules are not as strict as are GDI's.
* In particular, the 3D DDI doesn't care how we handle 'tie-breakers,'
* where the true line falls exactly mid-way between two pixels, as long
* as we're consistent, particularly between unclipped and clipped lines.
*
* For this implementation, I've chosen to match GDI's convention so that
* I can share clipping code with the GDI line drawing routines.
*
* For integer lines, GDI's rule is that when a line falls exactly mid-way
* between two pixels, the upper or left pixel should be left.  For
* standard Bresenham, this means that we should bias the error term down
* by an infinitesimal amount in the following octants:
*
*         \   0 | 1   /
*           \   |   /
*         0   \ | /   0
*         -------------        1 - Indicates the quadrants in which
*         1   / | \   1            the error term should be biased
*           /   |   \
*         /   0 | 1   \
*
* The MGA's convention for numbering the qudrants is as follows, where
* 'sdydxl_MAJOR_X' = 1, 'sdxl_SUB' = 2, 'sdy_SUB' = 4:
*
*         \   2 | 0   /
*           \   |   /
*         3   \ | /   1
*         -------------
*         7   / | \   5
*           /   |   \
*         /   6 | 4   \
*
**************************************************************************/

LONG gaiLineBias[] = { 1, 1, 0, 1, 1, 0, 0, 0 };

/******************************Public*Routine******************************\
* VOID vLineToTrivial
*
* Draws a single solid integer-only unclipped cosmetic line.
*
* We can't use the point-to-point capabilities of the MGA because its
* tie-breaker convention doesn't match that of NT's in two octants,
* and would cause us to fail HCTs.
*
\**************************************************************************/

VOID vLineToTrivial(
PDEV*       ppdev,
LONG        x,              // Passed in x1
LONG        y,              // Passed in y1
LONG        dx,             // Passed in x2
LONG        dy,             // Passed in y2
ULONG       iSolidColor,
MIX         mix)
{
    BYTE*   pjBase;
    FLONG   flQuadrant;
    ULONG   ulHwMix;

    pjBase = ppdev->pjBase;

    CHECK_FIFO_SPACE(pjBase, 13);

    if (mix == 0x0d0d)      // R2_COPYPEN
    {
        CP_WRITE(pjBase, DWG_DWGCTL, blockm_OFF + pattern_OFF + bltmod_BFCOL +
                         transc_BG_TRANSP + atype_RPL + bop_SRCCOPY);
    }
    else
    {
        ulHwMix = (mix & 0xff) - 1;

        CP_WRITE(pjBase, DWG_DWGCTL, blockm_OFF + pattern_OFF + bltmod_BFCOL +
                         transc_BG_TRANSP + atype_RSTR + (ulHwMix << 16));
    }

    CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, iSolidColor));

    if (!(GET_CACHE_FLAGS(ppdev, PATTERN_CACHE)))
    {
        CP_WRITE(pjBase, DWG_SRC0, 0xFFFFFFFF);
        CP_WRITE(pjBase, DWG_SRC1, 0xFFFFFFFF);
        CP_WRITE(pjBase, DWG_SRC2, 0xFFFFFFFF);
        CP_WRITE(pjBase, DWG_SRC3, 0xFFFFFFFF);
    }

    ppdev->HopeFlags = PATTERN_CACHE;

    CP_START(pjBase, DWG_XDST, x);
    CP_START(pjBase, DWG_YDST, y);

    flQuadrant = sdydxl_MAJOR_X;

    dx -= x;
    if (dx < 0)
    {
        dx = -dx;
        flQuadrant |= sdxl_SUB;
    }

    dy -= y;
    if (dy < 0)
    {
        dy = -dy;
        flQuadrant |= sdy_SUB;
    }

    if (dy > dx)
    {
        register LONG l;

        l  = dy;
        dy = dx;
        dx = l;                     // Swap 'dx' and 'dy'
        flQuadrant &= ~sdydxl_MAJOR_X;
    }

    CP_WRITE(pjBase, DWG_SGN, flQuadrant);
    CP_WRITE(pjBase, DWG_LEN, dx);
    CP_WRITE(pjBase, DWG_AR0, dy);
    CP_WRITE(pjBase, DWG_AR2, dy - dx);
    CP_START(pjBase, DWG_AR1, (dy + dy - dx - gaiLineBias[flQuadrant]) >> 1);
}

/******************************Public*Routine******************************\
* VOID vLineToClipped
*
* Draws a single solid integer-only clipped cosmetic line using
* 'New MM I/O'.
*
\**************************************************************************/

VOID vLineToClipped(
PDEV*       ppdev,
LONG        x1,
LONG        y1,
LONG        x2,
LONG        y2,
ULONG       iSolidColor,
MIX         mix,
RECTL*      prclClip)
{
    vSetClipping(ppdev, prclClip);
    vLineToTrivial(ppdev, x1, y1, x2, y2, iSolidColor, mix);
    vResetClipping(ppdev);
}

/******************************Public*Routine******************************\
* BOOL DrvLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix)
*
* Draws a single solid integer-only cosmetic line.
*
\**************************************************************************/

BOOL DrvLineTo(
SURFOBJ*    pso,
CLIPOBJ*    pco,
BRUSHOBJ*   pbo,
LONG        x1,
LONG        y1,
LONG        x2,
LONG        y2,
RECTL*      prclBounds,
MIX         mix)
{
    PDEV*   ppdev;
    DSURF*  pdsurf;
    OH*     poh;
    LONG    xOffset;
    LONG    yOffset;
    BOOL    bRet;

    // Pass the surface off to GDI if it's a device bitmap that we've
    // converted to a DIB:

    pdsurf = (DSURF*) pso->dhsurf;
    if (pdsurf->dt == DT_DIB)
    {
        return(EngLineTo(pdsurf->pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix));
    }

    // We'll be drawing to the screen or an off-screen DFB; copy the surface's
    // offset now so that we won't need to refer to the DSURF again:

    poh   = pdsurf->poh;
    ppdev = (PDEV*) pso->dhpdev;

    xOffset = poh->x;
    yOffset = poh->y;

    x1 += xOffset;
    x2 += xOffset;
    y1 += yOffset;
    y2 += yOffset;

    bRet = TRUE;

    if (pco == NULL)
    {
        vLineToTrivial(ppdev, x1, y1, x2, y2, pbo->iSolidColor, mix);
    }
    else if ((pco->iDComplexity <= DC_RECT) &&
             (prclBounds->left   >= MIN_INTEGER_BOUND) &&
             (prclBounds->top    >= MIN_INTEGER_BOUND) &&
             (prclBounds->right  <= MAX_INTEGER_BOUND) &&
             (prclBounds->bottom <= MAX_INTEGER_BOUND))
    {
        ppdev->xOffset = xOffset;
        ppdev->yOffset = yOffset;

        vLineToClipped(ppdev, x1, y1, x2, y2, pbo->iSolidColor, mix,
                                  &pco->rclBounds);
    }
    else
    {
        bRet = FALSE;
    }

    return(bRet);
}