//----------------------------------------------------------------------------
//
// line.cpp
//
// Line processing.
//
// Copyright (C) Microsoft Corporation, 1997.
//
//----------------------------------------------------------------------------

#include "pch.cpp"
#pragma hdrstop

DBG_DECLARE_FILE();

//----------------------------------------------------------------------------
//
// LinePatternStateMachine
//
// Runs the line pattern state machine and returns TRUE if the pixel is to be
// drawn, false otherwise.
//
//----------------------------------------------------------------------------

static inline BOOL LinePatternStateMachine(WORD wRepeatFactor, WORD wLinePattern, WORD& wRepeati, WORD& wPatterni)
{
    if (wRepeatFactor == 0)
    {
        return TRUE;
    }
    WORD wBit = (wLinePattern >> wPatterni) & 1;
    if (++wRepeati >= wRepeatFactor)
    {
        wRepeati = 0;
        wPatterni = (wPatterni+1) & 0xf;
    }
    return (BOOL)wBit;
}

#define CLAMP_COLOR(fVal, uVal) \
    if (FLOAT_LTZ(fVal))        \
    {                           \
        uVal = 0;               \
    }                           \
    else                        \
    {                           \
        if (uVal > 0xffff)      \
        {                       \
            uVal = 0xffff;      \
        }                       \
    }                           \

#define CLAMP_Z(fVal, uVal)     \
    if (FLOAT_LTZ(fVal))        \
    {                           \
        uVal = 0;               \
    }                           \

//----------------------------------------------------------------------------
//
// ClampPixel
//
// Clamp color, specular and z(if any) of a pixel. Right now, it's done for 
// first and last pixel of a line only.
//
//----------------------------------------------------------------------------
inline void
ClampPixel(PATTRSET pAttrs, PD3DI_RASTSPAN pSpan)
{
    CLAMP_COLOR(pAttrs->fB, pSpan->uB);
    CLAMP_COLOR(pAttrs->fG, pSpan->uG);
    CLAMP_COLOR(pAttrs->fR, pSpan->uR);
    CLAMP_COLOR(pAttrs->fA, pSpan->uA);
    CLAMP_COLOR(pAttrs->fBS, pSpan->uBS);
    CLAMP_COLOR(pAttrs->fGS, pSpan->uGS);
    CLAMP_COLOR(pAttrs->fRS, pSpan->uRS);
    CLAMP_Z(pAttrs->fZ, pSpan->uZ);
}

//----------------------------------------------------------------------------
//
// WalkLinePattern
//
// Walks a line and generates the pixels touched according to the pattern.
// If wRepeatFactor >= 1, we are patterning, otherwise, we are not
//
//----------------------------------------------------------------------------

HRESULT
WalkLinePattern(PSETUPCTX pStpCtx, WORD wRepeatFactor, WORD wLinePattern)
{
    HRESULT hr;
    UINT uSpansAvail;
    PD3DI_RASTSPAN pSpan;
    WORD wRepeati = 0;
    WORD wPatterni = 0;
    BOOL bFirst = TRUE;

    RSASSERT(pStpCtx->cLinePix > 0);

    hr = D3D_OK;
    uSpansAvail = 0;

    RSASSERT((pStpCtx->uFlags & PRIMSF_LOD_USED) == 0);
#ifdef PWL_FOG
    pStpCtx->uPwlFlags = PWL_NO_NEXT_FOG;
#endif

    for (;;)
    {
        if (pStpCtx->iX >= pStpCtx->pCtx->Clip.left &&
            pStpCtx->iX < pStpCtx->pCtx->Clip.right &&
            pStpCtx->iY >= pStpCtx->pCtx->Clip.top &&
            pStpCtx->iY < pStpCtx->pCtx->Clip.bottom)
        {
            if (LinePatternStateMachine(wRepeatFactor, wLinePattern, wRepeati, wPatterni))
            {
                if (uSpansAvail == 0)
                {
                    uSpansAvail = pStpCtx->cLinePix;
                    hr = ALLOC_SPANS(pStpCtx, &uSpansAvail, &pSpan);
                    if (hr != D3D_OK)
                    {
                        // uSpansAvail is set to zero on failure.
                        goto EH_Exit;
                    }
                }
                else
                {
                    pSpan++;
                }
                uSpansAvail--;
                pStpCtx->pPrim->uSpans++;

                pSpan->uPix = 1;
                pSpan->uX = (UINT16)pStpCtx->iX;
                pSpan->uY = (UINT16)pStpCtx->iY;

                pStpCtx->pfnFillSpanAttrs(&pStpCtx->Attr, pSpan, pStpCtx, 1);
                // Clamp first/last pixel
                if (bFirst || pStpCtx->cLinePix == 1)
                {
                    bFirst = FALSE;
                    ClampPixel(&pStpCtx->Attr, pSpan);
                }
            }
        }

        if (--pStpCtx->cLinePix == 0)
        {
            break;
        }

#ifdef VERBOSE_LINES
        RSDPF(("  %4d,%4d: %10d %11d => ",
               pStpCtx->iX, pStpCtx->iY,
               pStpCtx->iLineFrac, pStpCtx->iLineFrac + pStpCtx->iDLineFrac));
#endif

        pStpCtx->iLineFrac += pStpCtx->iDLineFrac;
        if (pStpCtx->iLineFrac < 0)
        {
            pStpCtx->iLineFrac &= 0x7fffffff;

            pStpCtx->iX += pStpCtx->iDXCY;
            pStpCtx->iY += pStpCtx->iDYCY;

            pStpCtx->DAttrDMajor.ipSurface = pStpCtx->DAttrCY.ipSurface;
            pStpCtx->DAttrDMajor.ipZ = pStpCtx->DAttrCY.ipZ;
        }
        else
        {
            pStpCtx->iX += pStpCtx->iDXNC;
            pStpCtx->iY += pStpCtx->iDYNC;

            pStpCtx->DAttrDMajor.ipSurface = pStpCtx->DAttrNC.ipSurface;
            pStpCtx->DAttrDMajor.ipZ = pStpCtx->DAttrNC.ipZ;
        }

#ifdef VERBOSE_LINES
        RSDPFM((DBG_MASK_FORCE | DBG_MASK_NO_PREFIX, "%4d,%4d: %10d\n",
               pStpCtx->iX, pStpCtx->iY,
               pStpCtx->iLineFrac));
#endif

        pStpCtx->pfnAddAttrs(&pStpCtx->Attr, &pStpCtx->DAttrDMajor, pStpCtx);
    }

 EH_Exit:
    if (uSpansAvail > 0)
    {
        FREE_SPANS(pStpCtx, uSpansAvail);
    }

    return hr;
}

//----------------------------------------------------------------------------
//
// PrimProcessor::Line
//
// Provides a line for processing.
//
//----------------------------------------------------------------------------

HRESULT
PrimProcessor::Line(LPD3DTLVERTEX pV0,
                    LPD3DTLVERTEX pV1,
                    LPD3DTLVERTEX pFlatVtx)
{
    HRESULT hr;

    hr = D3D_OK;

#if DBG
    hr = ValidateVertex(pV0);
    if (hr != D3D_OK)
    {
        return hr;
    }
    hr = ValidateVertex(pV1);
    if (hr != D3D_OK)
    {
        return hr;
    }
#endif

    // Clear per-line flags.
    m_StpCtx.uFlags &= ~(PRIMF_ALL | LNF_ALL);

    RSDPFM((RSM_FLAGS, "m_uPpFlags: 0x%08X, m_StpCtx.uFlags: 0x%08X\n",
            m_uPpFlags, m_StpCtx.uFlags));

    RSDPFM((RSM_LINES, "Line\n"));
    RSDPFM((RSM_LINES, "  V0 (%f,%f,%f)\n",
            pV0->dvSX, pV0->dvSY, pV0->dvSZ));
    RSDPFM((RSM_LINES, "  V1 (%f,%f,%f)\n",
            pV1->dvSX, pV1->dvSY, pV1->dvSZ));

    // Remember flat color controlling vertex for setup.
    m_StpCtx.pFlatVtx = pFlatVtx;

    if (LineSetup(pV0, pV1))
    {
        // Compute initial buffer pointers for the scanline.
        m_StpCtx.Attr.pSurface = m_StpCtx.pCtx->pSurfaceBits +
            m_StpCtx.iX * m_StpCtx.pCtx->iSurfaceStep +
            m_StpCtx.iY * m_StpCtx.pCtx->iSurfaceStride;
        if (m_StpCtx.uFlags & PRIMSF_Z_USED)
        {
            m_StpCtx.Attr.pZ = m_StpCtx.pCtx->pZBits +
                m_StpCtx.iX * m_StpCtx.pCtx->iZStep +
                m_StpCtx.iY * m_StpCtx.pCtx->iZStride;
        }

        // Line walking only generates single-pixel spans so
        // the prim deltas are unused.  Therefore, line spans
        // are simply added to whatever primitive happens to
        // be sitting in the buffer.

        hr = AppendPrim();
        if (hr != D3D_OK)
        {
            return hr;
        }

        union
        {
            D3DLINEPATTERN LPat;
            DWORD dwLPat;
        } LinePat;
        LinePat.dwLPat = m_StpCtx.pCtx->pdwRenderState[D3DRENDERSTATE_LINEPATTERN];
        hr = WalkLinePattern(&m_StpCtx, LinePat.LPat.wRepeatFactor, LinePat.LPat.wLinePattern);
    }

    return hr;
}