/******************************Module*Header**********************************\
 *
 *                           *******************
 *                           * GDI SAMPLE CODE *
 *                           *******************
 *
 * Module Name: clip.c
 *
 * Clipping code.
 *
 * 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"
#include "clip.h"

//----------------------------*Public*Routine-------------------------------
// BOOL bIntersect
//
// Function:
//      Check the integration of two input rectangles (RECTL* pRcl1,
//      RECTL* pRcl2) and set the intersection result in (RECTL* pRclResult)
//
// Return:
//      TRUE---If 'prcl1' and 'prcl2' intersect. The intersection will be in
//            'prclResult'
//      FALSE--If they don't intersect. 'prclResult' is undefined.
//
//---------------------------------------------------------------------------
BOOL
bIntersect(RECTL*  pRcl1,
           RECTL*  pRcl2,
           RECTL*  pRclResult)
{
    DBG_GDI((7, "bIntersect called--pRcl1=0x%x, pRcl2=0x%x, pRclResult=0x%x",
            pRcl1, pRcl2, pRclResult));
    
    pRclResult->left  = max(pRcl1->left,  pRcl2->left);
    pRclResult->right = min(pRcl1->right, pRcl2->right);

    //
    // Check if there an intersection horizontally
    //
    if ( pRclResult->left < pRclResult->right )
    {
        pRclResult->top    = max(pRcl1->top,    pRcl2->top);
        pRclResult->bottom = min(pRcl1->bottom, pRcl2->bottom);

        if (pRclResult->top < pRclResult->bottom)
        {
            //
            // Check if there an intersection vertically
            //
            return(TRUE);
        }
    }

    DBG_GDI((7, "bIntersect returned FALSE"));

    //
    // Return FALSE if there is no intersection
    //
    return(FALSE);
}// bIntersect()

//-----------------------------Public Routine-------------------------------
// LONG cIntersect
//
// This routine takes a list of rectangles from 'pRclIn' and clips them
// in-place to the rectangle 'pRclClip'.  The input rectangles don't
// have to intersect 'prclClip'; the return value will reflect the
// number of input rectangles that did intersect, and the intersecting
// rectangles will be densely packed.
//
//--------------------------------------------------------------------------
LONG
cIntersect(RECTL*  pRclClip,
           RECTL*  pRclIn,
           LONG    lNumOfRecs)
{
    LONG    cIntersections;
    RECTL*  pRclOut;

    DBG_GDI((7, "cIntersect called--pRclClip=0x%x, pRclIn=0x%x,lNumOfRecs=%ld",
             pRclClip, pRclIn, lNumOfRecs));

    cIntersections = 0;
    pRclOut        = pRclIn;        // Put the result in place as the input

    //
    // Validate input parameter
    //
    ASSERTDD( ((pRclIn != NULL ) && (pRclClip != NULL) && ( lNumOfRecs >= 0 )),
              "Wrong input to cIntersect" );    

    for (; lNumOfRecs != 0; pRclIn++, lNumOfRecs--)
    {
        pRclOut->left  = max(pRclIn->left,  pRclClip->left);
        pRclOut->right = min(pRclIn->right, pRclClip->right);

        if ( pRclOut->left < pRclOut->right )
        {
            //
            // Find intersection, horizontally, between current rectangle and
            // the clipping rectangle.
            //
            pRclOut->top    = max(pRclIn->top,    pRclClip->top);
            pRclOut->bottom = min(pRclIn->bottom, pRclClip->bottom);

            if ( pRclOut->top < pRclOut->bottom )
            {
                //
                // Find intersection, vertically, between current rectangle and
                // the clipping rectangle. Put this rectangle in the result
                // list and increment the counter. Ready for next input
                //
                pRclOut++;
                cIntersections++;
            }
        }
    }// loop through all the input rectangles

    DBG_GDI((7, "cIntersect found %d intersections", cIntersections));
    return(cIntersections);
}// cIntersect()

//-----------------------------Public Routine-------------------------------
// VOID vClipAndRender
//
// Clips the destination rectangle calling pfgn (the render function) as
// appropriate.
//
// Argumentes needed from function block (GFNPB)
// 
// pco------pointer to clip object
// prclDst--pointer to destination rectangle
// psurfDst-pointer to destination Surf
// psurfSrc-pointer to destination Surf (NULL if no source)
// pptlSrc--pointer to source point
// prclSrc--pointer to source rectangle (used if pptlSrc == NULL)
// pgfn-----pointer to render function
//
// NOTES:
//
// pptlSrc and prclSrc are only used if psurfSrc == psurfDst.  If there is
// no source psurfSrc must be set to NULL.  If prclSrc is specified, pptlSrc
// is not used.
//
//--------------------------------------------------------------------------

VOID vClipAndRender(GFNPB * ppb)
{
    CLIPOBJ * pco = ppb->pco;
    
    if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
    {
        ppb->pRects = ppb->prclDst;
        ppb->lNumRects = 1;
        ppb->pgfn(ppb);
    }
    else if (pco->iDComplexity == DC_RECT)
    {
        RECTL   rcl;

        if (bIntersect(ppb->prclDst, &pco->rclBounds, &rcl))
        {
            ppb->pRects = &rcl;
            ppb->lNumRects = 1;
            ppb->pgfn(ppb);
        }
    }
    else
    {
        ClipEnum    ce;
        LONG        c;
        BOOL        bMore;
        ULONG       ulDir = CD_ANY;

        // determine direction if operation on same surface
        if(ppb->psurfDst == ppb->psurfSrc)
        {
            LONG   lXSrc, lYSrc, offset;

            if(ppb->pptlSrc != NULL)
            {
                lXSrc = ppb->pptlSrc->x;
                lYSrc = ppb->pptlSrc->y;
            }
            else
            {
                lXSrc = ppb->prclSrc->left;
                lYSrc = ppb->prclSrc->top;
            }

            // NOTE: we can safely shift by 16 because the surface
            //       stride will never be greater the 2--16
            offset = (ppb->prclDst->top - lYSrc) << 16;
            offset += (ppb->prclDst->left - lXSrc);
            if(offset > 0)
                ulDir = CD_LEFTUP;
            else
                ulDir = CD_RIGHTDOWN;
        }


        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, ulDir, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (ULONG*) &ce);

            c = cIntersect(ppb->prclDst, ce.arcl, ce.c);

            if (c != 0)
            {
                ppb->pRects = ce.arcl;
                ppb->lNumRects = c;
                ppb->pgfn(ppb);
            }

        } while (bMore);
    }
}



