/*////////////////////////////////////////////////////////////////////////
//                                                                      //
//              INTEL CORPORATION PROPRIETARY INFORMATION               //
//                                                                      //
//      This software is supplied under the terms of a license          //
//      agreement or nondisclosure agreement with Intel Corporation     //
//      and may not be copied or disclosed except in accordance         //
//      with the terms of that agreement.                               //
//                                                                      //
////////////////////////////////////////////////////////////////////////*/


/*******************************************************************
 *
 *      FILE:            dciexfns.c
 *
 *    DESCRIPTION:    OffScreen Suurface callback functions
 *
 *    AUTHOR:        Davis W. Frank
 *
 *      DATE:            1/26/1994
 *
 *******************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <dciddi.h>
#include "dcihdw.h"
#include "debug.h"

#define macFreeMem(handle)    \
if (handle) {                \
    GlobalUnlock(handle);    \
    GlobalFree(handle);        \
    handle = NULL;            \
}


int i = 0;

/* Extended Surface callbacks */
DCIRVAL CALLBACK _loadds BeginAccessOff(LPVOID, LPRECT);
void    CALLBACK _loadds EndAccessOff(LPVOID);
void    CALLBACK _loadds DestroySurfaceOff(LPVOID);
DCIRVAL CALLBACK _loadds DrawOff(LPVOID);
DCIRVAL CALLBACK _loadds SetCliplistOff(LPVOID, LPRGNDATA);
DCIRVAL CALLBACK _loadds SetDestinationOff(LPVOID, LPRECT, LPRECT);

/* Local functions */
int InitializeInfoList( LPRGNDATA lpRgnData, LPCLIPRECTINFO lpInfo, 
                            WORD wcWidth, WORD wcBPP, int num, RECT FAR * rScreen);
void DCISampIntersectRects( LPRECT lprInt, LPRECTL lpRect1, LPRECT lpRect2 );

/*
 ** BeginAccessOff
 *
 *  Parameters:     lpSurface       FAR pointer to OFFINSTINFO passed back from
 *                                  DCICreateOffScreenSurface
 *                  lpR             FAR pointer to RECT describing, in screen
 *                                  coordinates, the destination rectangle
 *                                  for this Surface.  This is passed so
 *                                  the cursor can be excluded from this
 *                                  screen rectangle
 *
 *  Description:    Called to let the driver know that drawing to the screen is 
 *                  about to occur.
 *
 *  Returns:        DCI_STATUS flag to indicate changes to the DCISURFACEINFO,
 *                  if any
 *
 *  Side Effects:   DCI Client knows that Surface is accessible
 */

DCIRVAL CALLBACK _loadds BeginAccessOff(LPVOID lpSurface, LPRECT r)
{
    LPOFFINSTINFO  oinst = (LPOFFINSTINFO)lpSurface;

    ODS("BeginAccessOff\n");
    return DCI_OK;
}


/*
 ** EndAccessOff
 *
 *  Parameters:        lpSurface    FAR pointer to OFFINSTINFO passed back from
 *                                     DCICreateOffScreenSurface
 *
 *  Description:    Called to let the driver know that drawing to the screen is 
 *                    done.  Used in offscreen cases to let driver know to transfer
 *                    data to onscreen memory.
 *
 *  Returns:        none.
 *
 *    Side Effects:    none.
 */

void CALLBACK _loadds EndAccessOff(LPVOID lpSurface)
{
    LPOFFINSTINFO  oinst = (LPOFFINSTINFO)lpSurface;
    
    ODS("EndAccessOff\n");
    return;
}


/*
 ** DestroySurfaceOff
 *
 *  Parameters:        lpSurface    FAR pointer to OFFINSTINFO passed back from
 *                                     DCICreateOffScreenSurface
 *
 *  Description:    Sent to DCI surface provider to tell it to free up any memory
 *                    associated with this surface, and close down the surface.
 *
 *  Returns:        none.
 *
 *    Side Effects:    Frees memory associated with surface.
 */
void CALLBACK _loadds DestroySurfaceOff(LPVOID lpSurface)
{
    LPOFFINSTINFO  oinst = (LPOFFINSTINFO)lpSurface;    
    char        str[10];

    ODS("DestroySurfaceOff\n");

	wsprintf((LPSTR)str, "%4d\n", ++i);
	ODS(str);

    /* check for bogus surface. */
    if (!lpSurface)  
	{
		ODS("DCISAMP - cannot destroy NULL offscreen surface\n");    
        return;
    }

    /* free Extended surface */
    if (oinst->hOffSurf)
    {
    	GlobalUnlock(oinst->hOffSurf);
    	GlobalFree(oinst->hOffSurf);
    	oinst->hOffSurf = NULL;
    }

    /* free ClipBlit buffer */
    if (oinst->hCBlit)
    {
    	GlobalUnlock(oinst->hCBlit);
    	GlobalFree(oinst->hCBlit);
    	oinst->hCBlit = NULL;
    }

    /* clipping memory */
    if (oinst->hClipInfo)
    {
    	GlobalUnlock(oinst->hClipInfo);
    	GlobalFree(oinst->hClipInfo);
    	oinst->hClipInfo = NULL; 
    }

    /* free OFFINSTINFO */
    if (oinst->hOffInfo)
    {
        GlobalUnlock(oinst->hOffInfo);
        GlobalFree(oinst->hOffInfo);
    }
    
    if (oinst)
    	GlobalFreePtr(oinst);
    
    return;                 
}



/*
 ** DrawOff
 *
 *  Parameters:        lpSurface    FAR pointer to OFFINSTINFO passed back from
 *                                     DCICreateOffScreenSurface
 *
 *  Description:    Informs the DCI Provider that DCI Client is ready for data to
 *                    be transferred to the screen
 *
 *  Returns:        DCI_OK or a suitable DCI return code
 *
 *    Side Effects:    data in OffScreen surface is copied to the screen
 */

DCIRVAL CALLBACK _loadds DrawOff(LPVOID lpSurface)
{
    LONG         cSrcWidth, cSrcHeight, cDstWidth, cDstHeight;
    LPOFFINSTINFO  oinst = (LPOFFINSTINFO)lpSurface;
    DWORD        dwSrcOffset;
    DWORD       dwStrideSurf;
    
    ODS("DrawOff\n");

    /* check for NULL surface, in case client is a bonehead. */
    if (!lpSurface)
	return DCI_FAIL_INVALIDSURFACE;

    /* Check to see if Dest window is fully obscured -- no data
	needs to be copied to screen, so return                      */
    if (oinst->fClipState == CS_OBSCURED)
	return DCI_OK;

    /* calc widths and heights of Src & Dst */
    cSrcWidth  = oinst->rSrc.right - oinst->rSrc.left;
    cSrcHeight = oinst->rSrc.bottom - oinst->rSrc.top;
    cDstWidth  = oinst->rDst.right - oinst->rDst.left;
    cDstHeight = oinst->rDst.bottom - oinst->rDst.top;
    dwSrcOffset = oinst->rSrc.top * oinst->OffSurface.dciInfo.lStride + 
		  oinst->rSrc.left;
	   
    /* get surface stride */
    dwStrideSurf = (DWORD)(oinst->OffSurface.dciInfo.lStride);

    /* if not clipped, then stretch directly to frame buffer, else
	stretch to clip buffer and then copy only the rects from the
	Cliplist to the frame buffer.  Insert any hardware function
	to Blit/Stretch/Color convert from YUV here                        */
    if (!(oinst->OffSurface.dciInfo.dwCompression))
    {
      if (oinst->fClipState == CS_NOCLIP)
      {
	    /* This function actually copies the data to the frame buffer    */
	    StretchBlit(oinst->OffSurface.dciInfo.wSelSurface, 
			dwSrcOffset, dwStrideSurf,
			oinst->wSelScrn, oinst->dwOffScrn,
			(DWORD)(oinst->lStrideScrn),
			(DWORD)cSrcWidth, (DWORD)cSrcHeight,
			(DWORD)(LPBYTE)(oinst->bXcode), (DWORD)(LPBYTE)(oinst->bYcode), cDstWidth);
      }
      else
      {
	    /* This function actually copies the data first to another offscreen
		buffer, stretching if necessary.                                     */
	    StretchBlit(oinst->OffSurface.dciInfo.wSelSurface, 
			dwSrcOffset, dwStrideSurf,
			oinst->wSelCBlit, oinst->dwOffCBlit,
			(DWORD)cDstWidth,
			(DWORD)cSrcWidth, (DWORD)cSrcHeight,
			(DWORD)(LPBYTE)(oinst->bXcode), (DWORD)(LPBYTE)(oinst->bYcode), cDstWidth);
	    /* This function takes the 2nd offscreen buffer, applies the cliplist
		so it copies ONLY the rectangles in the cliplist to the screen.        */
	    if (oinst->fClipState != CS_OBSCURED)
		ClipBlit(oinst);
	    else 
		ODS("draw-obscured\n");
      }
    }
    else
    {
	    IF09TO422(oinst->OffSurface.dciInfo.wSelSurface,
			0L,
			oinst->wSelScrn,
			oinst->dwOffScrn,        
			(DWORD)cSrcWidth,
			(DWORD)cSrcHeight,
			(DWORD)(oinst->lStrideScrn));
    }    
    return DCI_OK;
}


/*
 ** SetCliplistOff
 *
 *  Parameters:        lpSurface    FAR pointer to OFFINSTINFO passed back from
 *                                     DCICreateOffScreenSurface
 *                    lpRgnData    FAR pointer to LPRGNDATA structure that
 *                                    contains Cliplist
 *
 *  Description:    The DCI Provider MUST support this function.  A DCI Client 
 *                    will call this function when a new a new Cliplist has 
 *                    been generated.  If this OffScreen Surface is combined
 *                    to the screen using a chroma key, then it is the responsibility
 *                    of the Provider to paint the appropriate areas at this
 *                    function. 
 *
 *  Returns:        DCI_OK or a suitable DCI return code
 *
 *    Side Effects:    If needed, chroma key data painted to screen
 */
DCIRVAL CALLBACK _loadds SetCliplistOff(LPVOID lpSurface, LPRGNDATA lpRgnData)
{
    LPOFFINSTINFO  oinst = (LPOFFINSTINFO)lpSurface;
              
              
    ODS("SetClipListOff\n");
	
    /* check for bogus surface pointer. */
    if (!lpSurface)
	    return DCI_FAIL_INVALIDSURFACE;

    /* check for bogus clip list. */
    if (!lpRgnData)
        return DCI_ERR_INVALIDCLIPLIST;

    /* Check oinst to see if rSrc and rDst are set.  If not,
       SetDestination() hasn't been callled -- return w/ an
       error
    */
    if ((oinst->rSrc.left == 0) && (oinst->rSrc.top == 0) && 
        (oinst->rSrc.right == 0) && (oinst->rSrc.bottom == 0) && 
        (oinst->rDst.left == 0) && (oinst->rDst.top == 0) && 
        (oinst->rDst.right == 0) && (oinst->rDst.bottom == 0))
        return DCI_ERR_INVALIDPOSITION;

    /* save screen rect. offsets */
    oinst->dwTop = lpRgnData->rdh.rcBound.top;
    oinst->dwLeft = lpRgnData->rdh.rcBound.left;

    /* check for empty cliplst: Image is completely obscured */
    if (!(lpRgnData->rdh.nCount))
    {    
        oinst->fClipState = CS_OBSCURED;
        return DCI_OK;
    }

    /* check for first rect == Dest rect: Image is not clipped */
    if ((((LPRECTL)(lpRgnData->Buffer))[0].left   == (long)(oinst->rDst.left))   && 
		(((LPRECTL)(lpRgnData->Buffer))[0].top    == (long)(oinst->rDst.top))    && 
		(((LPRECTL)(lpRgnData->Buffer))[0].bottom == (long)(oinst->rDst.bottom)) && 
		(((LPRECTL)(lpRgnData->Buffer))[0].right  == (long)(oinst->rDst.right))    )
		oinst->fClipState = CS_NOCLIP;
    else
    {
		oinst->fClipState = CS_CLIPPED;
        /* Convert the Cliplist into InfoList -- Data structure
           that allows ClipBlit() to be fast */    
        oinst->iInfoWall = InitializeInfoList( lpRgnData, oinst->lpClipInfo, 
    							(WORD) (oinst->rDst.right - oinst->rDst.left), 
     						    (WORD) (oinst->OffSurface.dciInfo.dwBitCount>>3),
    							(int) lpRgnData->rdh.nCount,
                                &(oinst->rDst));               
                                
		oinst->rBound.left = (int)lpRgnData->rdh.rcBound.left;
		oinst->rBound.top = (int)lpRgnData->rdh.rcBound.top;
		oinst->rBound.right = (int)lpRgnData->rdh.rcBound.right;
		oinst->rBound.bottom = (int)lpRgnData->rdh.rcBound.bottom;
		
    }
    
    /* If this OffScreen Surface is to be copied/comined to the screen
     * with a chroma key mechanism, the provider needs to paint the
     * chroma key data to the appropriate areas here.
     */        
    return DCI_OK;
}


/*
 ** SetDestinationOff
 *
 *  Parameters:        lpSurface    FAR pointer to OFFINSTINFO passed back from
 *                                     DCICreateOffScreenSurface
 *                    rDst        FAR pointer to a RECT describing, in screen
 *                                    coordinates, the destination rectangle
 *                                    of this surface
 *                    rSrc        FAR pointer to RECT describing the source
 *                                    rectangle of this surface
 *
 *  Description:    A DCI Client calls this function to notify the Provider 
 *                    of the destination rectangle for the surface data.  This
 *                    function must be called before the first call to 
 *                    BeginAccess.  
 *
 *  Returns:        DCI_OK, or a suitable DCI return code
 *
 *    Side Effects:    none
 */
DCIRVAL CALLBACK _loadds SetDestinationOff(LPVOID lpSurface, LPRECT rDst, LPRECT rSrc)
{
    LPOFFINSTINFO  oinst = (LPOFFINSTINFO)lpSurface;
    LONG         cSrcWidth, cSrcHeight, cDstWidth, cDstHeight;
    
    ODS("SetDestinationOff\n");

    /* check for NULL pointers.  This sucks, Beavis! */
    if ((!rDst) || (!rSrc) || (!lpSurface))
    	return DCI_FAIL_GENERIC;

    /* check for valid source rectangle, part 1: is it a sub-rectangle 
     * of our DCI surface ?
     */
    if ((rSrc->left < 0) || (rSrc->top < 0) ||
	((DWORD)rSrc->right > oinst->OffSurface.dciInfo.dwWidth) ||
	((DWORD)rSrc->bottom > oinst->OffSurface.dciInfo.dwHeight))
	    return DCI_ERR_INVALIDPOSITION;

    /* check for valid source rect., part 2: is it non-empty ? */
    if ((rSrc->right - rSrc->left <= 0) || 
	(rSrc->bottom - rSrc->top <= 0))
    	return DCI_ERR_INVALIDSTRETCH;

    /* check for valid dest. rect., non-empty */
    if ((rDst->right - rDst->left <= 0) || 
	(rDst->bottom - rDst->top <= 0))
	    return DCI_ERR_INVALIDSTRETCH;
	
    /* calc widths and heights of Src & Dst */
    cSrcWidth  = rSrc->right - rSrc->left;
    cSrcHeight = rSrc->bottom - rSrc->top;
    cDstWidth  = rDst->right - rDst->left;
    cDstHeight = rDst->bottom - rDst->top;


    /* don't support stretch in IF09, only in RGB8! */
    if (oinst->OffSurface.dciInfo.dwCompression)
    {
	    if ((cSrcWidth != cDstWidth) || (cSrcHeight != cDstHeight))
	        return DCI_ERR_INVALIDSTRETCH;
    }

    /* check for size of source rect. */
    if (cSrcWidth > DCISAMP_MAX_WIDTH)
    	return DCI_ERR_TOOBIGWIDTH;

    if (cSrcHeight > DCISAMP_MAX_HEIGHT)
	    return DCI_ERR_TOOBIGHEIGHT;

    /* set Source rectangle     */
    oinst->rSrc.left   = rSrc->left;
    oinst->rSrc.top    = rSrc->top;
    oinst->rSrc.right  = rSrc->right;
    oinst->rSrc.bottom = rSrc->bottom;
    
    /* set Destination rectangle      */
    oinst->rDst.left   = rDst->left;
    oinst->rDst.top    = rDst->top;
    oinst->rDst.right  = rDst->right;
    oinst->rDst.bottom = rDst->bottom;

    /* Calc code for X-stretch */
    CalculateStretchCode(cSrcWidth, cDstWidth, (LPBYTE)oinst->bXcode);
    /* Calc code for Y-stretch */
    CalculateStretchCode(cSrcHeight, cDstHeight, (LPBYTE)oinst->bYcode);
   	
    if (oinst->OffSurface.dciInfo.dwCompression)
		oinst->dwOffScrn = ((rDst->top)*oinst->lStrideScrn) +
		       ((rDst->left)*2);
    else
		oinst->dwOffScrn = ((rDst->top)*oinst->lStrideScrn) +
		       ((rDst->left)*((oinst->OffSurface.dciInfo.dwBitCount)>>3));
    
    return DCI_OK;
}


/*
 * InitializeInfoList(): takes a ClipList and turns it into a
 *                       CLIPRECTINFO list -- something that
 *                       UDHBlit() can speedily process
 *
 */
int InitializeInfoList( LPRGNDATA lpRgnData, LPCLIPRECTINFO lpInfo, 
                        WORD wcWidth, WORD wcBPP, int num, RECT FAR * rDest)
{
	int     i = 0, ii=0,
			j = 1,
			k = 0,
			iStart = 0,
			iMid,
			iEnd   = 0,   
			NextWall = 0,
			iLastWall  = 0;
	DWORD   dwcAccum = 0;
    LPRECTL lprClip;
    

    lprClip = (LPRECTL)lpRgnData->Buffer;
		
    // first rect in stripe is a Door record    
	while( ((lprClip[iEnd]).right  != 0) && ((lprClip[iEnd]).bottom != 0))
	{   
		// Place line difference in Door record
		if ( i==0 )     
			(lpInfo[i]).dwcGap = (lprClip[0]).top - (long)rDest->top;          
		else
			(lpInfo[i]).dwcGap = (lprClip[iStart]).top - (lprClip[iStart-1]).bottom;            
        
        // need the following to make sure dwfWall and dwcLen is always 0 as they 
        // are supposed to be; otherwise may mistakenly use old records
		(lpInfo[i]).dwcLen  = 0;
		(lpInfo[i]).dwfWall = 0;
		i++;
			
		// 2nd record in stripe is first "real" one, but must be counted from left edge
		(lpInfo[i]).dwcGap  = ((lprClip[iStart]).left - (long)rDest->left) * wcBPP;
		(lpInfo[i]).dwcLen  = ((lprClip[iStart]).right - (lprClip[iStart]).left) * wcBPP;
		(lpInfo[i]).dwfWall = 0;
		
		dwcAccum = (lpInfo[i]).dwcGap + (lpInfo[i]).dwcLen;
		i++;
		iEnd++;
		iMid = iStart;
		
		if (iEnd == num)
			// reached end of clip rect list  
    	    goto skipnext;
	
 		while( (lprClip[iStart]).bottom == (lprClip[iEnd]).bottom )
		{
			(lpInfo[i]).dwcGap  = ((lprClip[iEnd]).left - (lprClip[iMid]).right) * wcBPP;
			(lpInfo[i]).dwcLen  = ((lprClip[iEnd]).right - (lprClip[iEnd]).left) * wcBPP;
			(lpInfo[i]).dwfWall = 0;

			dwcAccum += (lpInfo[i]).dwcGap + (lpInfo[i]).dwcLen;            
			i++;            
			iMid++;         
			iEnd++; 
			if (iEnd == num)
				goto skipnext;
		} // end of stripe
skipnext:               
		// this CLIPRECTINFO is a 'Wall'.  Fill in accordingly
		(lpInfo[i]).dwcGap  = dwcAccum;
		(lpInfo[i]).dwcLen  = (lprClip[iStart]).bottom - (lprClip[iStart]).top;
		(lpInfo[i]).dwfWall = j;    // this will be index into WallKeeper
		
		iStart            = iEnd;   // set iStart to beginning of next stripe
		i++;
		j++;       
		if (iEnd == num)
			// reached end of clip rect list  
			break;
	} // finished w/ cliplist 

	iLastWall =  (i == 0) ? i : i-1;    
	// go thru lpInfo to each wall and fill in index of next Wall record
	NextWall=iLastWall;
	for (j=iLastWall-1; j > -1; j--)
	{   
	    if ( (lpInfo[j]).dwfWall != 0 )
		{   
		    (lpInfo[j]).dwfWall = NextWall;
			NextWall=j;
		}           
	}
						 
	// sets flag for Wall for last stripe
	(lpInfo[iLastWall]).dwfWall = (DWORD) -1L;
                                                               
	// return, in this param, the index of the 1st Wall record
	return NextWall;
}
