Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2763 lines
78 KiB

/*==========================================================================
*
* Copyright (C) 1997 Microsoft Corporation. All Rights Reserved.
*
* File: alphablt.c
* Content: DirectDraw Surface support for alpha-blended blt
* History:
* Date By Reason
* ==== == ======
* 30-sep-97 jvanaken Original version adapted from ddsblt.c
*
***************************************************************************/
#include "ddrawpr.h"
// function from ddraw module ddclip.c
extern HRESULT InternalGetClipList(LPDIRECTDRAWCLIPPER,
LPRECT,
LPRGNDATA,
LPDWORD,
LPDDRAWI_DIRECTDRAW_GBL);
#ifndef WINNT
#define DONE_BUSY() \
(*pdflags) &= ~BUSY;
#define LEAVE_BOTH_NOBUSY() \
{ if(pdflags) \
(*pdflags) &= ~BUSY; \
} \
LEAVE_BOTH();
#else
#define DONE_BUSY()
#define LEAVE_BOTH_NOBUSY() \
LEAVE_BOTH();
#endif
#define DONE_LOCKS() \
if (dest_lock_taken) \
{ \
InternalUnlock(surf_dest_lcl,NULL,NULL,0); \
dest_lock_taken = FALSE; \
} \
if (src_lock_taken && surf_src_lcl) \
{ \
InternalUnlock(surf_src_lcl,NULL,NULL,0); \
src_lock_taken = FALSE; \
}
#if defined(WIN95)
#define DONE_EXCLUDE() \
if (surf_dest_lcl->lpDDClipper != NULL) \
{ \
if ((pdrv->dwFlags & DDRAWI_DISPLAYDRV) && pdrv->dwPDevice && \
!(*pdrv->lpwPDeviceFlags & HARDWARECURSOR)) \
{ \
DD16_Unexclude(pdrv->dwPDevice); \
} \
}
#elif defined(WINNT)
#define DONE_EXCLUDE() ;
#endif
/*
* Stretch-blit info
*/
typedef struct
{
DWORD src_height;
DWORD src_width;
DWORD dest_height;
DWORD dest_width;
BOOL halonly; // HEL can't do this alpha-blit
BOOL helonly; // hardware driver can't do this alpha-blit
} STRETCH_BLT_INFO, FAR *LPSTRETCH_BLT_INFO;
/*
* Alpha-blitting capability bits
*/
typedef struct
{
// caps for hardware driver
DWORD dwCaps;
DWORD dwCKeyCaps;
DWORD dwFXCaps;
DWORD dwAlphaCaps;
DWORD dwFilterCaps;
// caps for HEL
DWORD dwHELCaps;
DWORD dwHELCKeyCaps;
DWORD dwHELFXCaps;
DWORD dwHELAlphaCaps;
DWORD dwHELFilterCaps;
// caps common to hardware driver and HEL
DWORD dwBothCaps;
DWORD dwBothCKeyCaps;
DWORD dwBothFXCaps;
DWORD dwBothAlphaCaps;
DWORD dwBothFilterCaps;
BOOL bHALSeesSysmem;
} ALPHA_BLT_CAPS, *LPALPHA_BLT_CAPS;
/*
* Return a pointer to the DDPIXELFORMAT structure that
* describes the specified surface's pixel format.
*/
static LPDDPIXELFORMAT getPixelFormatPtr(LPDDRAWI_DDRAWSURFACE_LCL surf_lcl)
{
LPDDPIXELFORMAT pDDPF;
if (surf_lcl == NULL)
{
return NULL;
}
if (surf_lcl->dwFlags & DDRAWISURF_HASPIXELFORMAT)
{
// surface contains explicitly defined pixel format
pDDPF = &surf_lcl->lpGbl->ddpfSurface;
}
else
{
// surface's pixel format is implicit -- same as primary's
pDDPF = &surf_lcl->lpSurfMore->lpDD_lcl->lpGbl->vmiData.ddpfDisplay;
}
return pDDPF;
} /* getPixelFormatPtr */
/*
* Initialize ALPHA_BLT_CAPS structure according to whether source and
* dest surfaces are in system or video (local or nonlocal) memory.
*/
static void initAlphaBltCaps(DWORD dwDstCaps,
DWORD dwSrcCaps,
LPDDRAWI_DIRECTDRAW_GBL pdrv,
LPALPHA_BLT_CAPS pcaps,
LPBOOL helonly)
{
DDASSERT(pcaps != NULL);
memset(pcaps, 0, sizeof(ALPHA_BLT_CAPS));
if ((dwSrcCaps | dwDstCaps) & DDSCAPS_NONLOCALVIDMEM &&
pdrv->ddCaps.dwCaps2 & DDCAPS2_NONLOCALVIDMEMCAPS)
{
/*
* At least one of the surfaces is nonlocal video memory. The device
* exports different capabilities for local and nonlocal video memory.
* If this is a nonlocal-to-local transfer then check the appropriate
* caps. Otherwise, force software emulation of the blit.
*/
if (dwSrcCaps & DDSCAPS_NONLOCALVIDMEM && dwDstCaps & DDSCAPS_LOCALVIDMEM)
{
/*
* Non-local to local video memory transfer.
*/
DDASSERT(NULL != pdrv->lpddNLVCaps);
DDASSERT(NULL != pdrv->lpddNLVHELCaps);
DDASSERT(NULL != pdrv->lpddNLVBothCaps);
/*
* We have specific caps for nonlocal video memory. Use them.
*/
pcaps->dwCaps = pdrv->lpddNLVCaps->dwNLVBCaps;
pcaps->dwCKeyCaps = pdrv->lpddNLVCaps->dwNLVBCKeyCaps;
pcaps->dwFXCaps = pdrv->lpddNLVCaps->dwNLVBFXCaps;
if (pdrv->lpddMoreCaps)
{
if (pcaps->dwFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwAlphaCaps = pdrv->lpddMoreCaps->dwAlphaCaps;
}
if (pcaps->dwFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwFilterCaps = pdrv->lpddMoreCaps->dwFilterCaps;
}
}
pcaps->dwHELCaps = pdrv->lpddNLVHELCaps->dwNLVBCaps;
pcaps->dwHELCKeyCaps = pdrv->lpddNLVHELCaps->dwNLVBCKeyCaps;
pcaps->dwHELFXCaps = pdrv->lpddNLVHELCaps->dwNLVBFXCaps;
if (pdrv->lpddHELMoreCaps)
{
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwHELAlphaCaps = pdrv->lpddHELMoreCaps->dwAlphaCaps;
}
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwHELFilterCaps = pdrv->lpddHELMoreCaps->dwFilterCaps;
}
}
pcaps->dwBothCaps = pdrv->lpddNLVBothCaps->dwNLVBCaps;
pcaps->dwBothCKeyCaps = pdrv->lpddNLVBothCaps->dwNLVBCKeyCaps;
pcaps->dwBothFXCaps = pdrv->lpddNLVBothCaps->dwNLVBFXCaps;
if (pdrv->lpddBothMoreCaps)
{
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwBothAlphaCaps = pdrv->lpddBothMoreCaps->dwAlphaCaps;
}
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwBothFilterCaps = pdrv->lpddBothMoreCaps->dwFilterCaps;
}
}
/*
* A driver that cannot filter is trivially capable of disabling filtering.
* By similar logic, a driver than cannot filter does not fail to respect
* the DDABLT_FILTERTRANSPBORDER flag unless filtering is explicitly enabled.
*/
if (!(pcaps->dwFXCaps & DDFXCAPS_BLTFILTER))
{
pcaps->dwFilterCaps = DDFILTCAPS_BLTCANDISABLEFILTER | DDFILTCAPS_BLTTRANSPBORDER;
pcaps->dwFXCaps |= DDFXCAPS_BLTFILTER;
}
if (!(pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER))
{
pcaps->dwHELFilterCaps = DDFILTCAPS_BLTCANDISABLEFILTER | DDFILTCAPS_BLTTRANSPBORDER;
pcaps->dwHELFXCaps |= DDFXCAPS_BLTFILTER;
}
if (!(pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER))
{
pcaps->dwBothFilterCaps = DDFILTCAPS_BLTCANDISABLEFILTER | DDFILTCAPS_BLTTRANSPBORDER;
pcaps->dwBothFXCaps |= DDFXCAPS_BLTFILTER;
}
pcaps->bHALSeesSysmem = FALSE;
return;
}
/*
* Nonlocal-to-nonlocal or local-to-nonlocal transfer. Force emulation.
*/
*helonly = TRUE;
}
if (!(pdrv->ddCaps.dwCaps & DDCAPS_CANBLTSYSMEM))
{
if ((dwSrcCaps | dwDstCaps) & DDSCAPS_SYSTEMMEMORY)
{
*helonly = TRUE;
}
}
if (dwSrcCaps & dwDstCaps & DDSCAPS_VIDEOMEMORY)
{
pcaps->dwCaps = pdrv->ddCaps.dwCaps;
pcaps->dwCKeyCaps = pdrv->ddCaps.dwCKeyCaps;
pcaps->dwFXCaps = pdrv->ddCaps.dwFXCaps;
if (pdrv->lpddMoreCaps)
{
if (pcaps->dwFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwAlphaCaps = pdrv->lpddMoreCaps->dwAlphaCaps;
}
if (pcaps->dwFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwFilterCaps = pdrv->lpddMoreCaps->dwFilterCaps;
}
}
pcaps->dwHELCaps = pdrv->ddHELCaps.dwCaps;
pcaps->dwHELCKeyCaps = pdrv->ddHELCaps.dwCKeyCaps;
pcaps->dwHELFXCaps = pdrv->ddHELCaps.dwFXCaps;
if (pdrv->lpddHELMoreCaps)
{
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwHELAlphaCaps = pdrv->lpddHELMoreCaps->dwAlphaCaps;
}
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwHELFilterCaps = pdrv->lpddHELMoreCaps->dwFilterCaps;
}
}
pcaps->dwBothCaps = pdrv->ddBothCaps.dwCaps;
pcaps->dwBothCKeyCaps = pdrv->ddBothCaps.dwCKeyCaps;
pcaps->dwBothFXCaps = pdrv->ddBothCaps.dwFXCaps;
if (pdrv->lpddBothMoreCaps)
{
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwBothAlphaCaps = pdrv->lpddBothMoreCaps->dwAlphaCaps;
}
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwBothFilterCaps = pdrv->lpddBothMoreCaps->dwFilterCaps;
}
}
pcaps->bHALSeesSysmem = FALSE;
}
else if ((dwSrcCaps & DDSCAPS_SYSTEMMEMORY) && (dwDstCaps & DDSCAPS_VIDEOMEMORY))
{
pcaps->dwCaps = pdrv->ddCaps.dwSVBCaps;
pcaps->dwCKeyCaps = pdrv->ddCaps.dwSVBCKeyCaps;
pcaps->dwFXCaps = pdrv->ddCaps.dwSVBFXCaps;
if (pdrv->lpddMoreCaps)
{
if (pcaps->dwFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwAlphaCaps = pdrv->lpddMoreCaps->dwSVBAlphaCaps;
}
if (pcaps->dwFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwFilterCaps = pdrv->lpddMoreCaps->dwSVBFilterCaps;
}
}
pcaps->dwHELCaps = pdrv->ddHELCaps.dwSVBCaps;
pcaps->dwHELCKeyCaps = pdrv->ddHELCaps.dwSVBCKeyCaps;
pcaps->dwHELFXCaps = pdrv->ddHELCaps.dwSVBFXCaps;
if (pdrv->lpddHELMoreCaps)
{
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwHELAlphaCaps = pdrv->lpddHELMoreCaps->dwSVBAlphaCaps;
}
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwHELFilterCaps = pdrv->lpddHELMoreCaps->dwSVBFilterCaps;
}
}
pcaps->dwBothCaps = pdrv->ddBothCaps.dwSVBCaps;
pcaps->dwBothCKeyCaps = pdrv->ddBothCaps.dwSVBCKeyCaps;
pcaps->dwBothFXCaps = pdrv->ddBothCaps.dwSVBFXCaps;
if (pdrv->lpddBothMoreCaps)
{
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwBothAlphaCaps = pdrv->lpddBothMoreCaps->dwSVBAlphaCaps;
}
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwBothFilterCaps = pdrv->lpddBothMoreCaps->dwSVBFilterCaps;
}
}
pcaps->bHALSeesSysmem = TRUE;
}
else if ((dwSrcCaps & DDSCAPS_VIDEOMEMORY) && (dwDstCaps & DDSCAPS_SYSTEMMEMORY))
{
pcaps->dwCaps = pdrv->ddCaps.dwVSBCaps;
pcaps->dwCKeyCaps = pdrv->ddCaps.dwVSBCKeyCaps;
pcaps->dwFXCaps = pdrv->ddCaps.dwVSBFXCaps;
if (pdrv->lpddMoreCaps)
{
if (pcaps->dwFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwAlphaCaps = pdrv->lpddMoreCaps->dwVSBAlphaCaps;
}
if (pcaps->dwFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwFilterCaps = pdrv->lpddMoreCaps->dwVSBFilterCaps;
}
}
pcaps->dwHELCaps = pdrv->ddHELCaps.dwVSBCaps;
pcaps->dwHELCKeyCaps = pdrv->ddHELCaps.dwVSBCKeyCaps;
pcaps->dwHELFXCaps = pdrv->ddHELCaps.dwVSBFXCaps;
if (pdrv->lpddHELMoreCaps)
{
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwHELAlphaCaps = pdrv->lpddHELMoreCaps->dwVSBAlphaCaps;
}
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwHELFilterCaps = pdrv->lpddHELMoreCaps->dwVSBFilterCaps;
}
}
pcaps->dwBothCaps = pdrv->ddBothCaps.dwVSBCaps;
pcaps->dwBothCKeyCaps = pdrv->ddBothCaps.dwVSBCKeyCaps;
pcaps->dwBothFXCaps = pdrv->ddBothCaps.dwVSBFXCaps;
if (pdrv->lpddBothMoreCaps)
{
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwBothAlphaCaps = pdrv->lpddBothMoreCaps->dwVSBAlphaCaps;
}
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwBothFilterCaps = pdrv->lpddBothMoreCaps->dwVSBFilterCaps;
}
}
pcaps->bHALSeesSysmem = TRUE;
}
else if (dwSrcCaps & dwDstCaps & DDSCAPS_SYSTEMMEMORY)
{
pcaps->dwCaps = pdrv->ddCaps.dwSSBCaps;
pcaps->dwCKeyCaps = pdrv->ddCaps.dwSSBCKeyCaps;
pcaps->dwFXCaps = pdrv->ddCaps.dwSSBFXCaps;
if (pdrv->lpddMoreCaps)
{
if (pcaps->dwFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwAlphaCaps = pdrv->lpddMoreCaps->dwSSBAlphaCaps;
}
if (pcaps->dwFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwFilterCaps = pdrv->lpddMoreCaps->dwSSBFilterCaps;
}
}
pcaps->dwHELCaps = pdrv->ddHELCaps.dwSSBCaps;
pcaps->dwHELCKeyCaps = pdrv->ddHELCaps.dwSSBCKeyCaps;
pcaps->dwHELFXCaps = pdrv->ddHELCaps.dwSSBFXCaps;
if (pdrv->lpddHELMoreCaps)
{
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwHELAlphaCaps = pdrv->lpddHELMoreCaps->dwSSBAlphaCaps;
}
if (pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwHELFilterCaps = pdrv->lpddHELMoreCaps->dwSSBFilterCaps;
}
}
pcaps->dwBothCaps = pdrv->ddBothCaps.dwSSBCaps;
pcaps->dwBothCKeyCaps = pdrv->ddBothCaps.dwSSBCKeyCaps;
pcaps->dwBothFXCaps = pdrv->ddBothCaps.dwSSBFXCaps;
if (pdrv->lpddBothMoreCaps)
{
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTALPHA)
{
pcaps->dwBothAlphaCaps = pdrv->lpddBothMoreCaps->dwSSBAlphaCaps;
}
if (pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER)
{
pcaps->dwBothFilterCaps = pdrv->lpddBothMoreCaps->dwSSBFilterCaps;
}
}
pcaps->bHALSeesSysmem = TRUE;
}
/*
* A driver that cannot filter is trivially capable of disabling filtering.
* By similar logic, a driver than cannot filter does not fail to respect
* the DDABLT_FILTERTRANSPBORDER flag unless filtering is explicitly enabled.
*/
if (!(pcaps->dwFXCaps & DDFXCAPS_BLTFILTER))
{
pcaps->dwFilterCaps = DDFILTCAPS_BLTCANDISABLEFILTER | DDFILTCAPS_BLTTRANSPBORDER;
pcaps->dwFXCaps |= DDFXCAPS_BLTFILTER;
}
if (!(pcaps->dwHELFXCaps & DDFXCAPS_BLTFILTER))
{
pcaps->dwHELFilterCaps = DDFILTCAPS_BLTCANDISABLEFILTER | DDFILTCAPS_BLTTRANSPBORDER;
pcaps->dwHELFXCaps |= DDFXCAPS_BLTFILTER;
}
if (!(pcaps->dwBothFXCaps & DDFXCAPS_BLTFILTER))
{
pcaps->dwBothFilterCaps = DDFILTCAPS_BLTCANDISABLEFILTER | DDFILTCAPS_BLTTRANSPBORDER;
pcaps->dwBothFXCaps |= DDFXCAPS_BLTFILTER;
}
} /* initAlphaBltCaps */
/*
* Verify that driver can perform requested stretching for blit.
*/
static HRESULT validateStretching(LPALPHA_BLT_CAPS pcaps,
LPSTRETCH_BLT_INFO psbi)
{
DWORD caps;
BOOL fail = FALSE;
/*
* Can we even stretch at all?
*/
if (!(pcaps->dwBothCaps & DDCAPS_BLTSTRETCH))
{
GETFAILCODEBLT(pcaps->dwCaps,
pcaps->dwHELCaps,
psbi->halonly,
psbi->helonly,
DDCAPS_BLTSTRETCH);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
}
if (psbi->helonly)
caps = pcaps->dwHELFXCaps;
else
caps = pcaps->dwFXCaps;
/*
* verify height
*/
if (psbi->src_height != psbi->dest_height)
{
if (psbi->src_height > psbi->dest_height)
{
/*
* can we shrink Y arbitrarily?
*/
if (!(caps & (DDFXCAPS_BLTSHRINKY)))
{
/*
* see if this is a non-integer shrink
*/
if ((psbi->src_height % psbi->dest_height) != 0)
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSHRINKY);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
/*
* see if we can integer shrink
*/
}
else if (!(caps & DDFXCAPS_BLTSHRINKYN))
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSHRINKYN);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
}
}
}
else
{
if (!(caps & DDFXCAPS_BLTSTRETCHY))
{
/*
* see if this is a non-integer stretch
*/
if ((psbi->dest_height % psbi->src_height) != 0)
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSTRETCHY);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
/*
* see if we can integer stretch
*/
}
else if (!(caps & DDFXCAPS_BLTSTRETCHYN))
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSTRETCHYN);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
}
}
}
}
/*
* verify width
*/
if (psbi->src_width != psbi->dest_width)
{
if (psbi->src_width > psbi->dest_width)
{
if (!(caps & DDFXCAPS_BLTSHRINKX))
{
/*
* Are we stretching by a non-integer factor?
*/
if ((psbi->src_width % psbi->dest_width) != 0)
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSHRINKX);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
/*
* see if we can integer shrink
*/
}
else if (!(caps & DDFXCAPS_BLTSHRINKXN))
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSHRINKXN);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
}
}
}
else
{
if (!(caps & DDFXCAPS_BLTSTRETCHX))
{
/*
* Are we stretching by a non-integer factor?
*/
if ((psbi->dest_width % psbi->src_width) != 0)
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSTRETCHX);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
}
if (!(caps & DDFXCAPS_BLTSTRETCHXN))
{
GETFAILCODEBLT(pcaps->dwFXCaps,
pcaps->dwHELFXCaps,
psbi->halonly,
psbi->helonly,
DDFXCAPS_BLTSTRETCHXN);
if (fail)
{
return DDERR_NOSTRETCHHW;
}
}
}
}
}
return DD_OK;
} /* validateStretching */
#undef DPF_MODNAME
#define DPF_MODNAME "AlphaBlt"
/*
* Wait for pending hardware operation on specified surface to finish.
*
* This function waits for the hardware driver to report that it has finished
* operating on the given surface. We should only call this function if the
* surface was a system memory surface involved in a DMA/busmastering transfer.
* Note this function clears the DDRAWISURFGBL_HARDWAREOPSTARTED flag.
*/
static void WaitForHardwareOp(LPDDRAWI_DIRECTDRAW_LCL pdrv_lcl,
LPDDRAWI_DDRAWSURFACE_LCL surf_lcl)
{
HRESULT hr;
#ifdef DEBUG
BOOL bSentMessage = FALSE;
DWORD dwStart = GetTickCount();
#endif
DDASSERT(surf_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY);
DPF(5, B, "Waiting for driver to finish with %08x", surf_lcl->lpGbl);
do
{
hr = InternalGetBltStatus(pdrv_lcl, surf_lcl, DDGBS_ISBLTDONE);
#ifdef DEBUG
if (GetTickCount() - dwStart >= 10000 && !bSentMessage)
{
bSentMessage = TRUE;
DPF_ERR("Driver error: Hardware op still pending on surface after 5 sec!");
}
#endif
} while (hr == DDERR_WASSTILLDRAWING);
DDASSERT(hr == DD_OK);
DPF(5, B, "Driver finished with that surface");
surf_lcl->lpGbl->dwGlobalFlags &= ~DDRAWISURFGBL_HARDWAREOPSTARTED;
} /* WaitForHardwareOp */
/*
* DD_Surface_AlphaBlt
*
* BitBLT from one surface to another with alpha blending.
*/
HRESULT DDAPI DD_Surface_AlphaBlt(
LPDIRECTDRAWSURFACE lpDDDestSurface,
LPRECT lpDestRect,
LPDIRECTDRAWSURFACE lpDDSrcSurface,
LPRECT lpSrcRect,
DWORD dwFlags,
LPDDALPHABLTFX lpDDAlphaBltFX)
{
struct
{
RGNDATAHEADER rdh;
RECT clipRect[8];
} myRgnBuffer;
DWORD rc;
LPDDRAWI_DDRAWSURFACE_INT surf_src_int;
LPDDRAWI_DDRAWSURFACE_LCL surf_src_lcl;
LPDDRAWI_DDRAWSURFACE_GBL surf_src;
LPDDRAWI_DDRAWSURFACE_INT surf_dest_int;
LPDDRAWI_DDRAWSURFACE_LCL surf_dest_lcl;
LPDDRAWI_DDRAWSURFACE_GBL surf_dest;
LPDDRAWI_DIRECTDRAW_LCL pdrv_lcl;
LPDDRAWI_DIRECTDRAW_GBL pdrv;
LPDDHAL_ALPHABLT bltfn;
DDHAL_BLTDATA bd;
STRETCH_BLT_INFO sbi;
BOOL fail;
BOOL dest_lock_taken=FALSE;
BOOL src_lock_taken=FALSE;
LPVOID dest_bits;
LPVOID src_bits;
HRESULT ddrval;
RECT rect;
ALPHA_BLT_CAPS caps;
LPWORD pdflags=0;
LPRGNDATA pRgn;
DDARGB ddargbScaleFactors;
DWORD dwFillValue;
DWORD dwDDPFDestFlags;
DWORD dwDDPFSrcFlags;
DDASSERT(sizeof(DDARGB)==sizeof(DWORD)); // we rely on this
ENTER_BOTH();
DPF(2,A,"ENTERAPI: DD_Surface_AlphaBlt");
TRY
{
ZeroMemory(&bd, sizeof(bd)); // initialize to zero
/*
* Validate surface pointers.
*/
surf_dest_int = (LPDDRAWI_DDRAWSURFACE_INT) lpDDDestSurface;
surf_src_int = (LPDDRAWI_DDRAWSURFACE_INT) lpDDSrcSurface;
if (!VALID_DIRECTDRAWSURFACE_PTR(surf_dest_int))
{
DPF_ERR("Invalid dest surface") ;
LEAVE_BOTH();
return DDERR_INVALIDOBJECT;
}
surf_dest_lcl = surf_dest_int->lpLcl;
surf_dest = surf_dest_lcl->lpGbl;
if (SURFACE_LOST(surf_dest_lcl))
{
DPF_ERR("Dest surface lost") ;
LEAVE_BOTH();
return DDERR_SURFACELOST;
}
if (surf_src_int != NULL)
{
if (!VALID_DIRECTDRAWSURFACE_PTR(surf_src_int))
{
DPF_ERR("Invalid source surface");
LEAVE_BOTH();
return DDERR_INVALIDOBJECT;
}
surf_src_lcl = surf_src_int->lpLcl;
surf_src = surf_src_lcl->lpGbl;
if (SURFACE_LOST(surf_src_lcl))
{
DPF_ERR("Src surface lost") ;
LEAVE_BOTH();
return DDERR_SURFACELOST;
}
}
else
{
surf_src_lcl = NULL;
surf_src = NULL;
}
if (dwFlags & ~DDABLT_VALID)
{
DPF_ERR("Invalid flags") ;
LEAVE_BOTH();
return DDERR_INVALIDPARAMS;
}
// Is the DONOTWAIT flag set?
if (dwFlags & DDABLT_DONOTWAIT)
{
if (dwFlags & DDABLT_WAIT)
{
DPF_ERR("WAIT and DONOTWAIT flags are mutually exclusive");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
else
{
// Unless the DONOTWAIT flag is explicitly set, use the default (WAIT).
dwFlags |= DDABLT_WAIT;
}
/*
* Set ARGB scaling factors and fill value to their default values.
* Note that setting ddargbScaleFactors to all ones effectively
* disables ARGB scaling, and a fill value of zero represents black.
*/
*(LPDWORD)&ddargbScaleFactors = ~0UL;
dwFillValue = 0;
/*
* Read parameters pointed to by lpDDAlphaBltFX argument.
*/
if (lpDDAlphaBltFX != 0)
{
if (IsBadWritePtr((LPVOID)lpDDAlphaBltFX, sizeof(DDALPHABLTFX)))
{
DPF_ERR("Argument lpDDAlphaBltFX is a bad pointer") ;
LEAVE_BOTH();
return DDERR_INVALIDPARAMS;
}
if (dwFlags & DDABLT_USEFILLVALUE)
{
dwFillValue = lpDDAlphaBltFX->dwFillValue;
}
else
{
ddargbScaleFactors = lpDDAlphaBltFX->ddargbScaleFactors;
}
}
// Is this a color-fill operation that uses dwFillValue?
if (dwFlags & DDABLT_USEFILLVALUE && surf_src_lcl == NULL)
{
// Could this possibly be an alpha-blended fill?
if (!(dwFlags & DDABLT_NOBLEND))
{
HRESULT hres;
// If the fill value is less than 100% opaque, we need to
// do an alpha fill rather than just a simple color fill.
// Convert physcolor to DDARGB value and test its opacity.
hres = ConvertFromPhysColor(
surf_dest_lcl,
&dwFillValue,
&ddargbScaleFactors);
if ((hres == DD_OK) && (ddargbScaleFactors.alpha != 255))
{
// The fill value is not 100% opaque, so do an alpha fill.
dwFlags &= ~DDABLT_USEFILLVALUE;
}
}
// Make sure DEGRADEARGBSCALING flag is not set.
if (dwFlags & DDABLT_DEGRADEARGBSCALING)
{
DPF_ERR("DEGRADEARGBSCALING and USEFILLVALUE flags are incompatible");
LEAVE_BOTH();
return DDERR_INVALIDPARAMS;
}
}
/*
* We do not allow blitting to or from an optimized surface.
*/
if (surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_OPTIMIZED ||
surf_src && surf_src_lcl->ddsCaps.dwCaps & DDSCAPS_OPTIMIZED)
{
DPF_ERR("Can't blt optimized surfaces") ;
LEAVE_BOTH();
return DDERR_INVALIDPARAMS;
}
pdrv = surf_dest->lpDD;
pdrv_lcl = surf_dest_lcl->lpSurfMore->lpDD_lcl;
#ifdef WINNT
// Update DDraw handle in driver GBL object.
pdrv->hDD = pdrv_lcl->hDD;
#endif
/*
* Default behavior is to automatically fail-over to software
* emulation if hardware driver cannot handle the specified
* blit. The DDABLT_HARDWAREONLY flag overrides this default.
*/
sbi.halonly = dwFlags & DDABLT_HARDWAREONLY;
sbi.helonly = dwFlags & DDABLT_SOFTWAREONLY;
/*
* Only the HEL can blit between two surfaces created by two
* different drivers.
*/
if (surf_src && surf_src->lpDD != pdrv &&
surf_src->lpDD->dwFlags & DDRAWI_DISPLAYDRV &&
pdrv->dwFlags & DDRAWI_DISPLAYDRV)
{
sbi.helonly = TRUE;
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPF_ERR("Exception encountered validating parameters");
LEAVE_BOTH();
return DDERR_INVALIDPARAMS;
}
#ifdef USE_ALIAS
if ((pdrv_lcl->lpDDCB->HALDDMiscellaneous2.AlphaBlt == NULL) &&
(pdrv->dwBusyDueToAliasedLock > 0))
{
/*
* Aliased locks (the ones that don't take the Win16 lock) don't
* set the busy bit either (it can't or USER gets very confused).
* However, we must prevent blits happening via DirectDraw as
* otherwise we get into the old host talking to VRAM while
* blitter does at the same time. Bad. So fail if there is an
* outstanding aliased lock just as if the BUSY bit had been set.
*/
DPF_ERR("Graphics adapter is busy (due to a DirectDraw lock)");
LEAVE_BOTH();
return DDERR_SURFACEBUSY;
}
#endif /* USE_ALIAS */
if(surf_src_lcl)
FlushD3DStates(surf_src_lcl); // Need to flush src because it could be a rendertarget
FlushD3DStates(surf_dest_lcl);
/*
* Test and set the busy bit. If it was already set, bail.
*/
#ifdef WIN95
{
BOOL isbusy = 0;
pdflags = pdrv->lpwPDeviceFlags;
_asm
{
mov eax, pdflags
bts word ptr [eax], BUSY_BIT
adc byte ptr isbusy,0
}
if (isbusy)
{
DPF(3, "BUSY - AlphaBlt");
LEAVE_BOTH();
return DDERR_SURFACEBUSY;
}
}
#endif
/*
* The following code was added to keep all of the HALs from
* changing their Blt() code when they add video port support.
* If the video port was using this surface but was recently
* flipped, we will make sure that the flip actually occurred
* before allowing access. This allows double buffered capture
* w/o tearing.
*/
if ((surf_src_lcl != NULL) &&
(surf_src_lcl->ddsCaps.dwCaps & DDSCAPS_VIDEOPORT))
{
LPDDRAWI_DDVIDEOPORT_INT lpVideoPort;
LPDDRAWI_DDVIDEOPORT_LCL lpVideoPort_lcl;
// Look at all video ports to see if any of them recently
// flipped from this surface.
lpVideoPort = pdrv->dvpList;
while(NULL != lpVideoPort)
{
lpVideoPort_lcl = lpVideoPort->lpLcl;
if (lpVideoPort_lcl->fpLastFlip == surf_src->fpVidMem)
{
// This can potentially tear - check the flip status
LPDDHALVPORTCB_GETFLIPSTATUS pfn;
DDHAL_GETVPORTFLIPSTATUSDATA GetFlipData;
LPDDRAWI_DIRECTDRAW_LCL pdrv_lcl;
pdrv_lcl = surf_src_lcl->lpSurfMore->lpDD_lcl;
pfn = pdrv_lcl->lpDDCB->HALDDVideoPort.GetVideoPortFlipStatus;
if (pfn != NULL) // Will simply tear if function not supported
{
GetFlipData.lpDD = pdrv_lcl;
GetFlipData.fpSurface = surf_src->fpVidMem;
KeepTrying:
rc = DDHAL_DRIVER_NOTHANDLED;
DOHALCALL(GetVideoPortFlipStatus, pfn, GetFlipData, rc, 0);
if ((DDHAL_DRIVER_HANDLED == rc) &&
(DDERR_WASSTILLDRAWING == GetFlipData.ddRVal))
{
if (dwFlags & DDABLT_WAIT)
{
goto KeepTrying;
}
LEAVE_BOTH_NOBUSY();
return DDERR_WASSTILLDRAWING;
}
}
}
lpVideoPort = lpVideoPort->lpLink;
}
}
TRY
{
/*
* Remove any cached run-length-encoded data for the source surface.
*/
if (surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
{
extern void FreeRleData(LPDDRAWI_DDRAWSURFACE_LCL); //in fasthel.c
FreeRleData(surf_dest_lcl);
}
/*
* Is either surface locked?
*/
if (surf_dest->dwUsageCount > 0 ||
surf_src != NULL && surf_src->dwUsageCount > 0)
{
DPF_ERR("Surface is locked");
LEAVE_BOTH_NOBUSY();
return DDERR_SURFACEBUSY;
}
BUMP_SURFACE_STAMP(surf_dest);
/*
* It is possible this function could be called in the middle
* of a mode change, in which case we could trash the frame buffer.
* To avoid regression, we will simply succeed the call without
* actually doing anything.
*/
if (pdrv->dwFlags & DDRAWI_CHANGINGMODE &&
!(surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY))
{
LEAVE_BOTH_NOBUSY()
return DD_OK;
}
/*
* Some parameters are valid only if a source surface is specified.
*/
if (surf_src == NULL)
{
/*
* No source surface is specified, so this must be a fill operation.
*/
if (dwFlags & (DDABLT_MIRRORLEFTRIGHT | DDABLT_MIRRORUPDOWN |
DDABLT_FILTERENABLE | DDABLT_FILTERDISABLE |
DDABLT_FILTERTRANSPBORDER | DDABLT_KEYSRC))
{
DPF_ERR("Specified flag requires source surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
if (lpSrcRect != NULL)
{
DPF_ERR("Source rectangle specified without source surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
else
{
/*
* A source surface is specified, so this must be a two-operand blit.
*/
if (dwFlags & DDABLT_USEFILLVALUE)
{
DPF_ERR("USEFILLVALUE flag incompatible with use of source surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
/*
* Get capability bits for source/dest memory combination.
*/
if (surf_src != NULL)
{
// initialize the blit caps according to the surface types
initAlphaBltCaps(surf_dest_lcl->ddsCaps.dwCaps,
surf_src_lcl->ddsCaps.dwCaps,
pdrv,
&caps,
&sbi.helonly);
}
else
{
/*
* No source surface. Use caps for vram-to-vram blits and choose
* hal or hel based on whether dest surface is in system memory.
* If the dest surface is in nonlocal video memory, we also force
* emulation as we don't currently support accelerated operation
* with nonlocal video memory as a target.
*/
initAlphaBltCaps(DDSCAPS_VIDEOMEMORY,
DDSCAPS_VIDEOMEMORY,
pdrv,
&caps,
&sbi.helonly);
if (surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY ||
surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_NONLOCALVIDMEM)
{
caps.bHALSeesSysmem = FALSE;
sbi.helonly = TRUE;
}
}
/*
* Can we really blit? -- Test DDCAPS_BLTCOLORFILL if src surf is null?
*/
if (!(caps.dwBothCaps & DDCAPS_BLT))
{
/*
* Unable to blit with both HEL and hardware driver.
* Can either of them do the blit?
*/
if (caps.dwCaps & DDCAPS_BLT)
{
sbi.halonly = TRUE; // hardware driver only
}
else if (caps.dwHELCaps & DDCAPS_BLT)
{
caps.bHALSeesSysmem = FALSE;
sbi.helonly = TRUE; // HEL only
}
else
{
DPF_ERR("Driver does not support blitting");
LEAVE_BOTH_NOBUSY();
return DDERR_NOBLTHW;
}
}
/*
* Validate height and width of destination rectangle.
*/
if (lpDestRect != NULL)
{
if (!VALID_RECT_PTR(lpDestRect))
{
DPF_ERR("Invalid dest rect specified");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDRECT;
}
bd.rDest = *(LPRECTL)lpDestRect;
}
else
{
MAKE_SURF_RECT(surf_dest, surf_dest_lcl, bd.rDest);
}
sbi.dest_height = bd.rDest.bottom - bd.rDest.top;
sbi.dest_width = bd.rDest.right - bd.rDest.left;
if (((int)sbi.dest_height <= 0) || ((int)sbi.dest_width <= 0))
{
DPF_ERR("Bad dest width or height -- must be positive and nonzero");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDRECT;
}
/*
* Validate height and width of source rectangle.
*/
if (surf_src != NULL)
{
/*
* Get source rectangle.
*/
if (lpSrcRect != NULL)
{
if (!VALID_RECT_PTR(lpSrcRect))
{
DPF_ERR("Invalid src rect specified");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDRECT;
}
bd.rSrc = *(LPRECTL)lpSrcRect;
}
else
{
MAKE_SURF_RECT(surf_src, surf_src_lcl, bd.rSrc);
}
sbi.src_height = bd.rSrc.bottom - bd.rSrc.top;
sbi.src_width = bd.rSrc.right - bd.rSrc.left;
if (((int)sbi.src_height <= 0) || ((int)sbi.src_width <= 0))
{
DPF_ERR("Bad source width or height -- must be positive and nonzero");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDRECT;
}
/*
* Multi-mon: Is this the primary for the desktop? This is the
* only case where the upper-left coord of the surface is not (0,0).
*/
if ((surf_src->lpDD->dwFlags & DDRAWI_VIRTUALDESKTOP) &&
(surf_src_lcl->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
{
if ((bd.rSrc.left < surf_src->lpDD->rectDevice.left) ||
(bd.rSrc.top < surf_src->lpDD->rectDevice.top) ||
(bd.rSrc.right > surf_src->lpDD->rectDevice.right)||
(bd.rSrc.bottom > surf_src->lpDD->rectDevice.bottom))
{
DPF_ERR("Source rect doesn't fit on Desktop");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDRECT;
}
}
else
{
if ((int)bd.rSrc.left < 0 ||
(int)bd.rSrc.top < 0 ||
(DWORD)bd.rSrc.bottom > (DWORD)surf_src->wHeight ||
(DWORD)bd.rSrc.right > (DWORD)surf_src->wWidth)
{
DPF_ERR("Invalid source rect specified");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDRECT;
}
}
/*
* Verify stretching...
*/
if (sbi.src_height != sbi.dest_height || sbi.src_width != sbi.dest_width)
{
HRESULT ddrval = validateStretching(&caps, &sbi);
if (ddrval != DD_OK)
{
DPF_ERR("Can't perform specified stretching");
LEAVE_BOTH_NOBUSY();
return ddrval;
}
/*
* Do source and dest rectangles lie on the same surface and overlap?
*/
if (surf_src_lcl == surf_dest_lcl &&
IntersectRect(&rect, (LPRECT)&bd.rSrc, (LPRECT)&bd.rDest))
{
DPF_ERR("Can't stretch if source/dest rectangles overlap");
LEAVE_BOTH_NOBUSY();
return DDERR_OVERLAPPINGRECTS;
}
}
}
/*
* Get pixel-format flags for source and destination surfaces.
*/
dwDDPFDestFlags = getPixelFormatPtr(surf_dest_lcl)->dwFlags;
if (surf_src_lcl != NULL)
{
dwDDPFSrcFlags = getPixelFormatPtr(surf_src_lcl)->dwFlags;
}
else
{
dwDDPFSrcFlags = 0;
}
/*
* Special Restrictions on Pixel Formats:
* -- If the surfaces have pixel formats that either are FOURCCs
* or are understood by the AlphaBlt HEL, no restrictions are
* imposed on the range of AlphaBlt features available for
* blit and fill operations. All formats that are understood
* by the HEL are listed in the PFTable array in ablthel.c.
* -- If either surface has a non-FOURCC pixel format that is not
* understood by AlphaBlt HEL, only a copy blit is permitted.
* For a copy blit, the source and dest formats are identical,
* and features such as stretching, mirroring, filtering, color
* keying, alpha blending, and ARGB scaling are not used.
*/
if ((!(dwDDPFDestFlags & DDPF_FOURCC) &&
(GetSurfPFIndex(surf_dest_lcl) == PFINDEX_UNSUPPORTED)) ||
((surf_src_lcl != NULL) && !(dwDDPFDestFlags & DDPF_FOURCC) &&
(GetSurfPFIndex(surf_src_lcl) == PFINDEX_UNSUPPORTED)))
{
LPDDPIXELFORMAT pDDPFDest = getPixelFormatPtr(surf_dest_lcl);
LPDDPIXELFORMAT pDDPFSrc = getPixelFormatPtr(surf_src_lcl);
/*
* This blit involves a non-FOURCC format that is unknown to the
* AlphaBlt HEL. In this case, we accept the blit operation only
* if it is a simple copy blit. It's okay if the rects overlap.
*/
if ((surf_src_lcl == NULL) || !doPixelFormatsMatch(pDDPFDest, pDDPFSrc))
{
DPF_ERR("Only copy blits are available with specified pixel format");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
// Is the DDABLT_NOBLEND flag specified?
if (!(dwFlags & DDABLT_NOBLEND))
{
DPF_ERR("NOBLEND flag is required to blit with specified pixel format");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
// Are any inappropriate DDABLT flags set?
if (dwFlags & (DDABLT_MIRRORUPDOWN | DDABLT_MIRRORLEFTRIGHT |
DDABLT_KEYSRC | DDABLT_DEGRADEARGBSCALING |
DDABLT_FILTERENABLE | DDABLT_FILTERTRANSPBORDER))
{
DPF_ERR("Specified DDABLT flag is incompatible with pixel format");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
// Is stretching required for this blit?
if (sbi.src_height != sbi.dest_height || sbi.src_width != sbi.dest_width)
{
DPF_ERR("Stretching is not permitted with specified pixel format");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
// Are the ARGB-scaling factors disabled (i.e., set to all ones)?
if (*(LPDWORD)&ddargbScaleFactors != ~0UL)
{
DPF_ERR("ARGB scaling must be disabled with specified pixel format");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
/*
* Do source and dest rectangles lie on the same surface and overlap?
*/
if (surf_src_lcl == surf_dest_lcl &&
IntersectRect(&rect, (LPRECT)&bd.rSrc, (LPRECT)&bd.rDest))
{
/*
* Yes, enforce restrictions on blits with overlapping rectangles.
*/
if (!(dwFlags & DDABLT_NOBLEND))
{
DPF_ERR("Can't blit between overlapping rects unless NOBLEND flag is set");
LEAVE_BOTH_NOBUSY();
return DDERR_OVERLAPPINGRECTS;
}
if (dwFlags & (DDABLT_MIRRORUPDOWN | DDABLT_MIRRORLEFTRIGHT |
DDABLT_KEYSRC | DDABLT_DEGRADEARGBSCALING |
DDABLT_FILTERENABLE | DDABLT_FILTERTRANSPBORDER))
{
DPF_ERR("Specified flag is illegal if source/dest rectangles overlap");
LEAVE_BOTH_NOBUSY();
return DDERR_OVERLAPPINGRECTS;
}
if (dwDDPFDestFlags & DDPF_FOURCC)
{
DPF_ERR("Overlapping source/dest rectangles illegal with FOURCC surface");
LEAVE_BOTH_NOBUSY();
return DDERR_OVERLAPPINGRECTS;
}
}
/*
* Does the destination surface have a FOURCC pixel format?
*/
if (dwDDPFDestFlags & DDPF_FOURCC)
{
// The DDABLT_USEFILLVALUE flag is illegal with a FOURCC dest surface.
if (dwFlags & DDABLT_USEFILLVALUE)
{
DPF_ERR("Can't use USEFILLVALUE flag with FOURCC dest surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
fail = FALSE; // initialize before using GETFAILCODEBLT macro
/*
* Validate source color key.
*/
if (dwFlags & DDABLT_KEYSRC)
{
DDASSERT(surf_src != NULL);
// make sure we can do this
if (!(caps.dwBothCKeyCaps & DDCKEYCAPS_SRCBLT))
{
GETFAILCODEBLT(caps.dwCKeyCaps,
caps.dwHELCKeyCaps,
sbi.halonly,
sbi.helonly,
DDCKEYCAPS_SRCBLT);
if (fail)
{
DPF_ERR("KEYSRC specified, not supported");
LEAVE_BOTH_NOBUSY();
return DDERR_NOCOLORKEYHW;
}
}
if (!(surf_src_lcl->dwFlags & DDRAWISURF_HASCKEYSRCBLT) ||
(dwDDPFSrcFlags & (DDPF_FOURCC | DDPF_ALPHAPIXELS)))
{
/*
* If the src color-key flag is set but the source surface has
* no associated src color key, just clear the flag instead of
* treating this as an error.
*/
dwFlags &= ~DDABLT_KEYSRC;
}
}
/*
* Validate up/down mirroring
*/
if (dwFlags & DDABLT_MIRRORUPDOWN)
{
DDASSERT(surf_src != NULL);
if (!(caps.dwBothFXCaps & DDFXCAPS_BLTMIRRORUPDOWN))
{
GETFAILCODEBLT(caps.dwFXCaps,
caps.dwHELFXCaps,
sbi.halonly,
sbi.helonly,
DDFXCAPS_BLTMIRRORUPDOWN);
if (fail)
{
DPF_ERR("Mirror up/down specified, not supported");
LEAVE_BOTH_NOBUSY();
return DDERR_NOMIRRORHW;
}
}
}
/*
* Validate left/right mirroring
*/
if (dwFlags & DDABLT_MIRRORLEFTRIGHT)
{
DDASSERT(surf_src != NULL);
if (!(caps.dwBothFXCaps & DDFXCAPS_BLTMIRRORLEFTRIGHT))
{
GETFAILCODEBLT(caps.dwFXCaps,
caps.dwHELFXCaps,
sbi.halonly,
sbi.helonly,
DDFXCAPS_BLTMIRRORLEFTRIGHT);
if (fail)
{
DPF_ERR("Mirror left/right specified, not supported");
LEAVE_BOTH_NOBUSY();
return DDERR_NOMIRRORHW;
}
}
}
/*
* Does destination surface have a palette-indexed pixel format?
*/
if (dwDDPFDestFlags & (DDPF_PALETTEINDEXED1 | DDPF_PALETTEINDEXED2 |
DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED8))
{
/*
* Is this a blit or a color-fill operation?
*/
if (surf_src_lcl == NULL)
{
/*
* Color-Fill: Palette-indexed dest is illegal without USEFILLVALUE flag.
*/
if (!(dwFlags & DDABLT_USEFILLVALUE))
{
DPF_ERR("USEFILLVALUE flag required to fill palette-indexed dest surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
else
{
/*
* Blit: Destination surface is palette-indexed, so we require source
* surface to have same pixel format as destination. (Note that this
* also makes color fills illegal to palette-indexed dest surfaces.)
*/
if (dwDDPFSrcFlags != dwDDPFDestFlags)
{
DPF_ERR("If dest is palette-indexed, source must have same pixel format");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
if (dwFlags & (DDABLT_FILTERENABLE | DDABLT_FILTERTRANSPBORDER))
{
DPF_ERR("Illegal to specify filtering with palette-indexed destination");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
if (*(LPDWORD)&ddargbScaleFactors != ~0UL)
{
DPF_ERR("Illegal to enable ARGB scaling with palette-indexed destination");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
/*
* If source and dest surfaces both have attached palettes, we require that
* they reference the same palette object. In a later release, we may relax
* this requirement in order to support color-table conversion or dithering.
*/
if ((surf_src_lcl->lpDDPalette != NULL) &&
(surf_dest_lcl->lpDDPalette != NULL) &&
(surf_src_lcl->lpDDPalette->lpLcl->lpGbl->lpColorTable !=
surf_dest_lcl->lpDDPalette->lpLcl->lpGbl->lpColorTable))
{
DPF_ERR("If source and dest surfaces both have palettes, must be same palette");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
}
else if (dwDDPFSrcFlags & (DDPF_PALETTEINDEXED1 | DDPF_PALETTEINDEXED2 |
DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED8) &&
(surf_src_lcl->lpDDPalette == NULL ||
surf_src_lcl->lpDDPalette->lpLcl->lpGbl->lpColorTable == NULL))
{
/*
* Conversion of source pixels to destination pixel format is
* impossible because source surface has no attached palette.
*/
DPF_ERR( "No palette associated with palette-indexed source surface" );
LEAVE_BOTH_NOBUSY();
return DDERR_NOPALETTEATTACHED;
}
/*
* We do no ARGB scaling if NOBLEND flag is set.
*/
if (dwFlags & DDABLT_NOBLEND)
{
if (dwFlags & DDABLT_DEGRADEARGBSCALING)
{
DPF_ERR("NOBLEND and DEGRADEARGBSCALING flags are incompatible");
LEAVE_BOTH();
return DDERR_INVALIDPARAMS;
}
if (surf_src != NULL && *(LPDWORD)&ddargbScaleFactors != ~0UL)
{
DPF_ERR("ARGB scaling of source surface illegal if NOBLEND flag is set");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
else if ((dwDDPFSrcFlags | dwDDPFDestFlags) & DDPF_ALPHAPIXELS)
{
/*
* We've been asked to perform a blit or fill that requires blending
* with the alpha-channel information in the pixel formats for one
* or both surfaces. Verify that the driver supports this.
*/
if (!(caps.dwBothFXCaps & DDFXCAPS_BLTALPHA))
{
GETFAILCODEBLT(caps.dwFXCaps,
caps.dwHELFXCaps,
sbi.halonly,
sbi.helonly,
DDFXCAPS_BLTALPHA);
if (fail)
{
DPF_ERR("Alpha-blended blit requested, but not supported");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
/*
* Verify that the driver supports surfaces whose pixel
* formats contain an alpha-channel component.
*/
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTALPHAPIXELS))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTALPHAPIXELS);
if (fail)
{
DPF_ERR("Alpha pixel format specified, but not supported");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
/*
* Does dest surface have alpha channel?
*/
if (dwDDPFDestFlags & DDPF_ALPHAPIXELS)
{
/*
* Verify that destination surface has a premultiplied-
* alpha pixel format. Non-premultiplied alpha won't do.
*/
if (!(dwDDPFDestFlags & DDPF_ALPHAPREMULT))
{
DPF_ERR("Illegal to blend with non-premultiplied alpha in dest surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
/*
* Verify that driver can handle premultiplied-alpha pixel format.
* (Dest surface is not allowed to be non-premultiplied alpha.)
*/
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTPREMULT))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTPREMULT);
if (fail)
{
DPF_ERR("No driver support for premultiplied alpha");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
/*
* Does source surface have alpha channel?
*/
if (dwDDPFSrcFlags & DDPF_ALPHAPIXELS)
{
/*
* Are we asking the driver to handle both ARGB scaling and a
* source alpha channel when it can't do both at the same time?
*/
if (*(LPDWORD)&ddargbScaleFactors != ~0 &&
!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTALPHAANDARGBSCALING) &&
!(dwFlags & DDABLT_DEGRADEARGBSCALING))
{
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTALPHAANDARGBSCALING))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTALPHAANDARGBSCALING);
if (fail)
{
DPF_ERR("No driver support for alpha channel and ARGB scaling in same blit");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
/*
* Are color components in pixel format premultiplied by the
* alpha component or not? In either case, verify that the
* driver supports the specified alpha format.
*/
if (dwDDPFSrcFlags & DDPF_ALPHAPREMULT)
{
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTPREMULT))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTPREMULT);
if (fail)
{
DPF_ERR("No driver support for premultiplied alpha");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
else
{
DWORD val = 0x01010101UL*ddargbScaleFactors.alpha;
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTNONPREMULT))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTNONPREMULT);
if (fail)
{
DPF_ERR("No driver support for non-premultiplied alpha");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
/*
* We allow only one-factor ARGB scaling with a source
* surface that has a non-premultiplied alpha pixel format.
* The following code enforces this rule.
*/
if (*(LPDWORD)&ddargbScaleFactors != val)
{
if (dwFlags & DDABLT_DEGRADEARGBSCALING)
{
*(LPDWORD)&ddargbScaleFactors = val;
}
else
{
DPF_ERR("Can't do 2 or 4-mult ARGB scaling with non-premultiplied alpha surface");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
}
}
}
}
/*
* If filtering is to be explicitly enabled or disabled, verify that
* the hardware driver is capable of performing the blit as requested.
*/
if (dwFlags & (DDABLT_FILTERENABLE | DDABLT_FILTERDISABLE | DDABLT_FILTERTRANSPBORDER))
{
/*
* Is driver capable of doing any kind of filtering at all?
*/
if (!(caps.dwBothFXCaps & DDFXCAPS_BLTFILTER))
{
GETFAILCODEBLT(caps.dwFXCaps,
caps.dwHELFXCaps,
sbi.halonly,
sbi.helonly,
DDFXCAPS_BLTFILTER);
if (fail)
{
DPF_ERR("No driver support for filtered blit");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
if (!(~dwFlags & (DDABLT_FILTERENABLE | DDABLT_FILTERDISABLE)))
{
DPF_ERR("Illegal to both enable and disable filtering");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
if (!(~dwFlags & (DDABLT_FILTERTRANSPBORDER | DDABLT_FILTERDISABLE)))
{
DPF_ERR("Illegal to set FILTERTRANSPBORDER if filtering is explicitly disabled");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
if ((dwFlags & DDABLT_FILTERENABLE) &&
!(caps.dwBothFilterCaps & DDFILTCAPS_BLTQUALITYFILTER))
{
GETFAILCODEBLT(caps.dwFilterCaps,
caps.dwHELFilterCaps,
sbi.halonly,
sbi.helonly,
DDFILTCAPS_BLTQUALITYFILTER);
if (fail)
{
DPF_ERR("No driver support for filtered blit");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
if ((dwFlags & DDABLT_FILTERDISABLE) &&
!(caps.dwBothFilterCaps & DDFILTCAPS_BLTCANDISABLEFILTER))
{
GETFAILCODEBLT(caps.dwFilterCaps,
caps.dwHELFilterCaps,
sbi.halonly,
sbi.helonly,
DDFILTCAPS_BLTCANDISABLEFILTER);
if (fail)
{
DPF_ERR("Driver cannot disable filtering for blits");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
if ((dwFlags & DDABLT_FILTERTRANSPBORDER) &&
!(caps.dwBothFilterCaps & DDFILTCAPS_BLTTRANSPBORDER))
{
GETFAILCODEBLT(caps.dwFilterCaps,
caps.dwHELFilterCaps,
sbi.halonly,
sbi.helonly,
DDFILTCAPS_BLTTRANSPBORDER);
if (fail)
{
DPF_ERR("Driver cannot filter with transparent border");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
/*
* Validate ARGB scaling factors.
*/
if (!(dwFlags & DDABLT_DEGRADEARGBSCALING) &&
*(LPDWORD)&ddargbScaleFactors != ~0UL &&
!(surf_src_lcl == NULL && ddargbScaleFactors.alpha == 255))
{
/*
* Some kind of ARGB scaling is specified. Can the driver
* do any kind of alpha blending at all?
*/
if (!(caps.dwBothFXCaps & DDFXCAPS_BLTALPHA))
{
GETFAILCODEBLT(caps.dwFXCaps,
caps.dwHELFXCaps,
sbi.halonly,
sbi.helonly,
DDFXCAPS_BLTALPHA);
if (fail)
{
DPF_ERR("ARGB scaling requested for blit, but not supported");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
/*
* We permit a color factor to be bigger than the alpha
* factor only if the hardware uses saturated arithmetic
* to prevent the calculated color value from overflowing.
*/
if (!(dwFlags & DDABLT_NOBLEND) &&
(ddargbScaleFactors.red > ddargbScaleFactors.alpha ||
ddargbScaleFactors.green > ddargbScaleFactors.alpha ||
ddargbScaleFactors.blue > ddargbScaleFactors.alpha))
{
/*
* Driver must be capable of doing saturated arithmetic.
*/
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTSATURATE))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTSATURATE);
if (fail)
{
// Neither the H/W driver nor HEL can handle it, so fail.
DPF_ERR("Driver can't do saturated arithmetic during alpha blending");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
/*
* Is this an alpha-blit or an alpha-fill operation?
*/
if (surf_src_lcl == NULL)
{
/*
* This is an alpha fill. Can the driver handle it?
*/
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTALPHAFILL))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTALPHAFILL);
if (fail)
{
// Neither the H/W driver nor HEL can handle it, so fail.
DPF_ERR("Driver can't do alpha-blended color-fill operation");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
else
{
/*
* Alpha blit. Can the driver handle any ARGB scaling at all?
*/
#define ARGBSCALINGBITS \
(DDALPHACAPS_BLTARGBSCALE1F | DDALPHACAPS_BLTARGBSCALE2F | DDALPHACAPS_BLTARGBSCALE4F)
if (!(caps.dwBothAlphaCaps & ARGBSCALINGBITS))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
ARGBSCALINGBITS);
if (fail)
{
// Neither the H/W driver nor HEL can handle it, so fail.
DPF_ERR("Driver can't handle any ARGB scaling at all");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
#undef ARGBSCALINGBITS
if (ddargbScaleFactors.red != ddargbScaleFactors.green ||
ddargbScaleFactors.red != ddargbScaleFactors.blue)
{
/*
* Driver must be capable of doing 4-factor ARGB scaling.
*/
if (!(caps.dwBothAlphaCaps & DDALPHACAPS_BLTARGBSCALE4F))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTARGBSCALE4F);
if (fail)
{
// Neither the H/W driver nor HEL can handle it, so fail.
DPF_ERR("Driver can't handle 4-factor ARGB scaling");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
else if (ddargbScaleFactors.red != ddargbScaleFactors.alpha)
{
/*
* Driver must be capable of doing 2-factor ARGB scaling.
*/
if (!(caps.dwBothAlphaCaps & (DDALPHACAPS_BLTARGBSCALE2F |
DDALPHACAPS_BLTARGBSCALE4F)))
{
GETFAILCODEBLT(caps.dwAlphaCaps,
caps.dwHELAlphaCaps,
sbi.halonly,
sbi.helonly,
DDALPHACAPS_BLTARGBSCALE2F |
DDALPHACAPS_BLTARGBSCALE4F);
if (fail)
{
// Neither the H/W driver nor HEL can handle it, so fail.
DPF_ERR("Driver can't handle 2-factor ARGB scaling");
LEAVE_BOTH_NOBUSY();
return DDERR_NOALPHAHW;
}
}
}
}
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPF_ERR("Exception encountered validating parameters");
LEAVE_BOTH_NOBUSY();
return DDERR_INVALIDPARAMS;
}
DDASSERT(!(sbi.halonly && sbi.helonly));
/*
* Are we permitted to degrade the specified ARGB-scaling operation
* to one the driver can handle?
*/
if (dwFlags & DDABLT_DEGRADEARGBSCALING)
{
DWORD dwFXCaps, dwAlphaCaps;
// Get the caps for the selected driver.
dwFXCaps = (sbi.helonly) ? caps.dwHELFXCaps : caps.dwFXCaps;
dwAlphaCaps = (sbi.helonly) ? caps.dwHELAlphaCaps : caps.dwAlphaCaps;
if (!(dwFXCaps & DDFXCAPS_BLTALPHA))
{
/*
* The driver should have done this anyway, but just in case...
*/
dwAlphaCaps = 0;
}
/*
* Is this a blit or a fill operation?
*/
if (surf_src_lcl == NULL)
{
/*
* This is a fill -- and possibly an alpha fill.
*/
if (!(dwAlphaCaps & DDALPHACAPS_BLTALPHAFILL))
{
/*
* The driver can't do an alpha fill, so we'll ask
* it to do just a simple color fill instead.
*/
ddargbScaleFactors.alpha = 255;
}
}
else
{
/*
* This is a blit. What are the driver's ARGB-scaling capabilities?
*/
if (!(dwAlphaCaps & (DDALPHACAPS_BLTARGBSCALE1F |
DDALPHACAPS_BLTARGBSCALE2F |
DDALPHACAPS_BLTARGBSCALE4F)))
{
/*
* Driver can't do any kind of ARGB scaling at all, so just
* disable ARGB scaling by setting all four factors to 255.
*/
*(LPDWORD)&ddargbScaleFactors = ~0UL;
}
else if (!(dwAlphaCaps & (DDALPHACAPS_BLTARGBSCALE2F |
DDALPHACAPS_BLTARGBSCALE4F)))
{
/*
* The driver can do only 1-factor ARGB scaling, so set the
* three color factors to the same value as the alpha factor.
*/
*(LPDWORD)&ddargbScaleFactors = 0x01010101UL*ddargbScaleFactors.alpha;
}
else if (!(dwAlphaCaps & DDALPHACAPS_BLTARGBSCALE4F))
{
/*
* Driver can do only 2-factor ARGB scaling, so make sure
* all three color factors are set to the same value.
*/
if (ddargbScaleFactors.red != ddargbScaleFactors.green ||
ddargbScaleFactors.red != ddargbScaleFactors.blue)
{
/*
* Set all three color factors to value F, which is the
* weighted average of their specified values (Fr,Fg,Fb):
* F = .299*Fr + .587*Fg + .114*Fb
*/
DWORD F = 19595UL*ddargbScaleFactors.red +
38470UL*ddargbScaleFactors.green +
7471UL*ddargbScaleFactors.blue;
ddargbScaleFactors.red =
ddargbScaleFactors.green =
ddargbScaleFactors.blue = (BYTE)(F >> 16);
}
}
if (!(dwAlphaCaps & DDALPHACAPS_BLTALPHAANDARGBSCALING))
{
/*
* Driver can't handle both a source alpha channel and ARGB scaling
* factors in the same blit operation, so just turn off ARGB scaling.
*/
*(LPDWORD)&ddargbScaleFactors = ~0UL;
}
}
/*
* Can driver do saturated arithmetic for alpha blit or alpha fill?
*/
if (!(dwAlphaCaps & DDALPHACAPS_BLTSATURATE))
{
/*
* Driver can't do saturated arithmetic, so make sure no
* no color factors exceed the value of the alpha factor.
*/
if (ddargbScaleFactors.red > ddargbScaleFactors.alpha)
{
ddargbScaleFactors.red = ddargbScaleFactors.alpha;
}
if (ddargbScaleFactors.green > ddargbScaleFactors.alpha)
{
ddargbScaleFactors.green = ddargbScaleFactors.alpha;
}
if (ddargbScaleFactors.blue > ddargbScaleFactors.alpha)
{
ddargbScaleFactors.blue = ddargbScaleFactors.alpha;
}
}
}
/*
* Tell the driver to do the blit.
*/
TRY
{
/*
* Finish loading blit data for HAL callback.
*/
bd.lpDD = pdrv;
bd.lpDDDestSurface = surf_dest_lcl;
bd.lpDDSrcSurface = surf_src_lcl;
bd.ddargbScaleFactors = ddargbScaleFactors;
bd.bltFX.dwSize = sizeof( DDBLTFX );
/*
* For the AlphaBlt callback, the rOrigDest and rOrigSrc members
* ALWAYS contain the original dest and source rects.
*/
bd.rOrigDest = bd.rDest;
bd.rOrigSrc = bd.rSrc;
/*
* The only AlphaBlt API flags that are propagated to the
* driver are those that have no Blt API equivalents.
*/
bd.dwAFlags = dwFlags & (DDABLT_FILTERENABLE | DDABLT_FILTERDISABLE |
DDABLT_FILTERTRANSPBORDER | DDABLT_NOBLEND);
/*
* This flag tells the driver that it's a source-over-dest operation.
* This flag is never passed by the Blt API, so drivers which have a
* unified DDI can distinguish who called them
*/
bd.dwAFlags |= DDABLT_SRCOVERDEST;
if (dwFlags & DDABLT_KEYSRC) // source color key?
{
bd.dwFlags |= DDBLT_KEYSRCOVERRIDE;
bd.bltFX.ddckSrcColorkey = surf_src_lcl->ddckCKSrcBlt;
}
if (dwFlags & (DDABLT_MIRRORLEFTRIGHT | DDABLT_MIRRORUPDOWN))
{
bd.dwFlags |= DDBLT_DDFX;
if (dwFlags & DDABLT_MIRRORLEFTRIGHT) //left-right mirroring?
{
bd.bltFX.dwDDFX |= DDBLTFX_MIRRORLEFTRIGHT;
}
if (dwFlags & DDABLT_MIRRORUPDOWN) // up-down mirroring?
{
bd.bltFX.dwDDFX |= DDBLTFX_MIRRORUPDOWN;
}
}
/*
* If the specified blit operation can be handled by the Blt HAL
* callback instead of by the AlphaBlt HAL callback, should it
* treat the blit as a color-fill or a source-copy operation?
*/
if (surf_src_lcl != NULL)
{
//it's a srccopy. Set flags appropriately
bd.dwFlags |= DDBLT_ROP;
bd.bltFX.dwROP = SRCCOPY;
bd.dwROPFlags = ROP_HAS_SOURCE; // 0x00000001
}
else
{
// This is a fill operation of some kind.
if (dwFlags & DDABLT_USEFILLVALUE)
{
HRESULT hres;
// The client specified a fill value in the dest pixel format.
bd.bltFX.dwFillColor = dwFillValue;
bd.dwFlags |= DDBLT_COLORFILL;
}
else if ((bd.ddargbScaleFactors.alpha == 255) || (dwFlags & DDABLT_NOBLEND))
{
// The client specified an alpha fill, but no alpha blending is
// required, so we can replace it with a simple color fill.
// convert the ARGB value to a physcolor:
HRESULT hres = ConvertToPhysColor(
surf_dest_lcl,
&bd.ddargbScaleFactors,
&bd.bltFX.dwFillColor);
// Make sure this is not a FOURCC or some other funny pixel format.
if (hres == DD_OK)
{
bd.dwFlags |= DDBLT_COLORFILL;
}
}
}
#ifdef WINNT
// Did the mode change since ENTER_DDRAW?
if (DdQueryDisplaySettingsUniqueness() != uDisplaySettingsUnique)
{
// mode changed, don't do the blt
DPF_ERR("Mode changed between ENTER_DDRAW and HAL call");
LEAVE_BOTH_NOBUSY()
return DDERR_SURFACELOST;
}
#endif
#if defined(WIN95)
/*
* Some drivers (like S3) do stuff in their BeginAccess call
* that screws up stuff that they did in their DDHAL Lock Call.
*
* Exclusion needs to happen BEFORE the lock call to prevent this.
*
*/
if (surf_dest_lcl->lpDDClipper != NULL)
{
/*
* exclude the mouse cursor.
*
* we only need to do this for the windows display driver
*
* we only need to do this if we are blitting to or from the
* primary surface.
*
* we only do this in the clipping case, we figure if the
* app cares enough to not scribble all over other windows
* he also cares enough to not to wipe out the cursor.
*
* we only need to do this if the driver is using a
* software cursor.
*
* NOTE
* we should check and only do this on the primary?
* we should make sure the clipper is window based?
* we should check for the source being the primary?
*
*/
if ((pdrv->dwFlags & DDRAWI_DISPLAYDRV) && pdrv->dwPDevice &&
!(*pdrv->lpwPDeviceFlags & HARDWARECURSOR) &&
(surf_dest->dwGlobalFlags & DDRAWISURFGBL_ISGDISURFACE) )
{
if (lpDDDestSurface == lpDDSrcSurface)
{
RECTL rcl;
UnionRect((RECT*)&rcl, (RECT*)&bd.rDest, (RECT*)&bd.rSrc);
DD16_Exclude(pdrv->dwPDevice, &rcl);
}
else
{
DD16_Exclude(pdrv->dwPDevice, &bd.rDest);
}
}
}
#endif
#ifdef WINNT
get_clipping_info:
#endif
/*
* Determine clipping region for destination surface.
*/
{
LPDIRECTDRAWCLIPPER pClipper;
RECT rcDestSurf;
pRgn = (LPRGNDATA)&myRgnBuffer; // this buffer's probably big enough
pClipper = (LPDIRECTDRAWCLIPPER)surf_dest_lcl->lpSurfMore->lpDDIClipper;
SetRect(&rcDestSurf, 0, 0, surf_dest->wWidth, surf_dest->wHeight);
if (pClipper == NULL)
{
/*
* The destination surface has no attached clipper.
* Set the clip region to a single rectangle the
* width and height of the primary surface.
*/
pRgn->rdh.nCount = 1; // default = a single clip rect
memcpy((LPRECT)&pRgn->Buffer, &rcDestSurf, sizeof(RECT));
/*
* Add a rect to the region list if this is a managed surface
*/
if(IsD3DManaged(surf_dest_lcl))
{
LPREGIONLIST lpRegionList = surf_dest_lcl->lpSurfMore->lpRegionList;
if(lpDestRect)
{
if(lpRegionList->rdh.nCount != NUM_RECTS_IN_REGIONLIST)
{
lpRegionList->rect[(lpRegionList->rdh.nCount)++] = bd.rDest;
lpRegionList->rdh.nRgnSize += sizeof(RECT);
if(bd.rDest.left < lpRegionList->rdh.rcBound.left)
lpRegionList->rdh.rcBound.left = bd.rDest.left;
if(bd.rDest.right > lpRegionList->rdh.rcBound.right)
lpRegionList->rdh.rcBound.right = bd.rDest.right;
if(bd.rDest.top < lpRegionList->rdh.rcBound.top)
lpRegionList->rdh.rcBound.top = bd.rDest.top;
if(bd.rDest.bottom > lpRegionList->rdh.rcBound.bottom)
lpRegionList->rdh.rcBound.bottom = bd.rDest.bottom;
}
}
else
{
/* Mark everything dirty */
lpRegionList->rdh.nCount = NUM_RECTS_IN_REGIONLIST;
}
}
}
else
{
DWORD rgnSize = 0;
LPDDRAWI_DIRECTDRAW_GBL pdrv = surf_dest_lcl->lpGbl->lpDD;
/*
* This surface has an attached clipper. Get the clip list.
*/
ddrval = InternalGetClipList(pClipper,
&rcDestSurf,
NULL, // we just want rgnSize
&rgnSize,
pdrv);
if (ddrval != DD_OK)
{
DPF_ERR("Couldn't get size of clip region");
LEAVE_BOTH_NOBUSY();
return DDERR_GENERIC;
}
if (rgnSize > sizeof(myRgnBuffer))
{
/*
* Statically allocated region buffer isn't big enough.
* Need to dynamically allocate a bigger buffer.
*/
pRgn = (LPRGNDATA)MemAlloc(rgnSize);
if (!pRgn)
{
// couldn't allocate memory for clip region
DPF_ERR("Can't allocate memory to buffer clip region");
LEAVE_BOTH_NOBUSY();
return DDERR_OUTOFMEMORY;
}
}
ddrval = InternalGetClipList(pClipper,
&rcDestSurf,
pRgn,
&rgnSize,
pdrv);
if (ddrval != DD_OK)
{
// can't get clip region
if (pRgn != (LPRGNDATA)&myRgnBuffer)
{
MemFree(pRgn);
}
DPF_ERR("Can't get dest clip region");
LEAVE_BOTH_NOBUSY();
return DDERR_GENERIC;
}
if(IsD3DManaged(surf_dest_lcl))
{
/* We don't want to deal with this mess, so mark everything dirty */
surf_dest_lcl->lpSurfMore->lpRegionList->rdh.nCount = NUM_RECTS_IN_REGIONLIST;
}
}
/*
* Load clipping info into data struct for HAL callback.
*/
bd.dwRectCnt = pRgn->rdh.nCount;
bd.prDestRects = (LPRECT)&pRgn->Buffer;
}
/*
* Does the driver have to do any clipping?
*/
if (bd.dwRectCnt > 1)
{
// Yes, clipping is (probably) required.
bd.IsClipped = TRUE;
}
else if (bd.dwRectCnt == 0)
{
// Window is completely obscured, so don't draw anything.
LEAVE_BOTH_NOBUSY();
return DD_OK;
}
else
{
/*
* The visibility region consists of a single clip rect.
* Is any portion of the destination rectangle visible?
*/
if (!IntersectRect((LPRECT)&bd.rDest, (LPRECT)&bd.rOrigDest,
&bd.prDestRects[0]))
{
// No portion of the destination rectangle is visible.
LEAVE_BOTH_NOBUSY();
return DD_OK;
}
/*
* Will the source rectangle have to be adjusted to
* compensate for the clipping of the dest rect?
*/
if (surf_src_lcl != NULL &&
!EqualRect((LPRECT)&bd.rDest, (LPRECT)&bd.rOrigDest))
{
// Yes, the source rect must be adjusted.
if (sbi.dest_width != sbi.src_width ||
sbi.dest_height != sbi.src_height)
{
/*
* The driver must do the clipping for a stretched blit
* because bd.rSrc permits us to express the adjusted
* source rect only to the nearest integer coordinates.
*/
bd.IsClipped = TRUE;
}
else
{
// We can do the clipping here for a nonstretched blit.
POINT p;
p.x = bd.rOrigSrc.left - bd.rOrigDest.left;
p.y = bd.rOrigSrc.top - bd.rOrigDest.top;
CopyRect((LPRECT)&bd.rSrc, (LPRECT)&bd.rDest);
OffsetRect((LPRECT)&bd.rSrc, p.x, p.y);
}
}
}
/*
* Older drivers may support the Blt callback, but not the AlphaBlt
* callback. One of these drivers may be able to perform the specified
* blit operation as long as it doesn't use any AlphaBlt-specific
* features such as alpha blending, ARGB scaling, or filtering.
* In this case, we can use the Blt callback to perform the blit.
* Decide which DDI to call. Start off assuming Alpha DDI
*/
bltfn = pdrv_lcl->lpDDCB->HALDDMiscellaneous2.AlphaBlt;
bd.dwFlags |= DDBLT_AFLAGS; // assume we'll use AlphaBlt callback
/*
* Check to see if we can pass this call to old blt DDI
*/
if ( !((dwDDPFDestFlags | dwDDPFSrcFlags) & DDPF_ALPHAPIXELS) &&
!(dwFlags & DDABLT_FILTERENABLE) )
{
// There are no alpha pixels involved. Maybe we can use the Blt DDI
if ( (bd.ddargbScaleFactors.alpha == 255) && (!sbi.helonly) )
{
LPDDPIXELFORMAT pDDPFDest = getPixelFormatPtr(surf_dest_lcl);
LPDDPIXELFORMAT pDDPFSrc = getPixelFormatPtr(surf_src_lcl);
// If this is a blit (and not a color fill), the source and dest pixel
// formats must be identical and the scaling factors must all be 1.0.
if ( (surf_src_lcl == NULL) ||
(!memcmp(pDDPFDest, pDDPFSrc, sizeof(DDPIXELFORMAT)) &&
(~0UL == *((LPDWORD)(&bd.ddargbScaleFactors)))) )
{
// Make sure the driver doesn't have to do any clipping. Also ensure
// that the driver does not require DDraw to pagelock sysmem surfaces.
if (!bd.IsClipped &&
(!caps.bHALSeesSysmem ||
pdrv->ddCaps.dwCaps2 & DDCAPS2_NOPAGELOCKREQUIRED))
{
// Verify that the driver supports the Blt HAL callback.
bltfn = (LPDDHAL_ALPHABLT) pdrv_lcl->lpDDCB->HALDDSurface.Blt;
if (bltfn)
{
bd.dwFlags &= ~DDBLT_AFLAGS; // we'll use Blt callback
if (surf_src_lcl == NULL)
{
DPF(4,"Calling Blt DDI for AlphaBlt color fill");
}
else
{
DPF(4,"Calling Blt DDI for AlphaBlt copy");
}
/*
* The following thunk address is used by the Blt callback,
* but is ignored by the AlphaBlt callback.
*/
bd.Blt = pdrv_lcl->lpDDCB->cbDDSurfaceCallbacks.Blt;
}
}
}
}
}
/*
* Set up for a HAL or a HEL call?
*/
if (bltfn == NULL)
{
/*
* Neither the alphablt nor blt ddi calls apply or aren't implemented
*/
sbi.helonly = TRUE;
}
if (sbi.helonly && sbi.halonly)
{
DPF_ERR("AlphaBlt not supported in software or hardware");
if (pRgn != (LPRGNDATA)&myRgnBuffer)
{
MemFree(pRgn); // this clip region was malloc'd
}
LEAVE_BOTH_NOBUSY();
return DDERR_NOBLTHW;
}
/*
* Can the hardware driver perform the blit?
*/
if (!sbi.helonly)
{
/*
* Yes, we're going to do a hardware-accelerated blit.
*/
DPF(4, "Hardware AlphaBlt");
/*
* The DDI was selected above
*/
//bd.AlphaBlt = NULL; // 32-bit call, no thunk
/*
* Tell the hardware driver to perform the blit. We may have to wait
* if the driver is still busy with a previous drawing operation.
*/
do
{
DOHALCALL_NOWIN16(AlphaBlt, bltfn, bd, rc, sbi.helonly);
if (rc != DDHAL_DRIVER_HANDLED || bd.ddRVal != DDERR_WASSTILLDRAWING)
{
break; // driver's finished for better or worse...
}
DPF(4, "Waiting...");
} while (dwFlags & DDABLT_WAIT);
/*
* Was the hardware driver able to handle the blit?
*/
if (rc == DDHAL_DRIVER_HANDLED)
{
#ifdef WINNT
if (bd.ddRVal == DDERR_VISRGNCHANGED)
{
if (pRgn != (LPRGNDATA)&myRgnBuffer)
{
MemFree(pRgn);
}
DPF(5,"Resetting VisRgn for surface %x", surf_dest_lcl);
DdResetVisrgn(surf_dest_lcl, (HWND)0);
goto get_clipping_info;
}
#endif
if (bd.ddRVal != DDERR_WASSTILLDRAWING)
{
/*
* Yes, the blit was handled by the hardware driver.
* If source or dest surface is in system memory, tag it so
* we know it's involved in an ongoing hardware operation.
*/
if (bd.ddRVal == DD_OK && caps.bHALSeesSysmem)
{
DPF(5,B,"Tagging surface %08x", surf_dest);
if (surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
surf_dest->dwGlobalFlags |= DDRAWISURFGBL_HARDWAREOPDEST;
if (surf_src)
{
DPF(5,B,"Tagging surface %08x", surf_src);
if (surf_src_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
surf_src->dwGlobalFlags |= DDRAWISURFGBL_HARDWAREOPSOURCE;
}
}
}
}
else
{
DDASSERT(rc == DDHAL_DRIVER_NOTHANDLED);
/*
* No, the hardware driver says it could not handle the blit.
* If sbi.halonly = FALSE, we'll let the HEL do the blit.
*/
sbi.helonly = TRUE; // force fail-over to HEL
}
}
/*
* Do we need to ask the HEL to perform the blit?
*/
if (sbi.helonly && !sbi.halonly)
{
/*
* Yes, we'll ask the HEL to do a software-emulated blit.
*/
bltfn = pdrv_lcl->lpDDCB->HELDDMiscellaneous2.AlphaBlt;
/*
* Is dest surface in system memory or video memory?
*/
if (surf_dest_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
{
/*
* Destination surface is in system memory.
* If this surface was involved in a hardware op, we need to
* probe the driver to see if it's done. NOTE: This assumes
* that only one driver can be responsible for a system-memory
* operation. See comment with WaitForHardwareOp.
*/
if (surf_dest->dwGlobalFlags & DDRAWISURFGBL_HARDWAREOPSTARTED)
{
WaitForHardwareOp(pdrv_lcl, surf_dest_lcl);
}
dest_lock_taken = FALSE;
}
else
{
/*
* Wait loop: Take write lock on dest surface in video memory.
*/
while(1)
{
ddrval = InternalLock(surf_dest_lcl, &dest_bits, NULL, 0);
if (ddrval == DD_OK)
{
GET_LPDDRAWSURFACE_GBL_MORE(surf_dest)->fpNTAlias = (FLATPTR)dest_bits;
break; // successfully locked dest surface
}
if (ddrval != DDERR_WASSTILLDRAWING)
{
/*
* Can't lock dest surface. Fail the call.
*/
if (pRgn != (LPRGNDATA)&myRgnBuffer)
{
MemFree(pRgn); // this clip region was malloc'd
}
DONE_EXCLUDE();
DONE_BUSY();
LEAVE_BOTH();
return ddrval;
}
}
dest_lock_taken = TRUE;
}
if (surf_src && surf_src != surf_dest)
{
/*
* Is source surface in system memory or video memory?
*/
if (surf_src_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
{
/*
* Source surface is in system memory.
* If this surface was involved in a hardware op, we need to
* probe the driver to see if it's done. NOTE: This assumes
* that only one driver can be responsible for a system-memory
* operation. See comment with WaitForHardwareOp.
*/
if (surf_src &&
surf_src_lcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY &&
surf_src->dwGlobalFlags & DDRAWISURFGBL_HARDWAREOPSTARTED)
{
WaitForHardwareOp(pdrv_lcl, surf_src_lcl);
}
src_lock_taken = FALSE;
}
else
{
/*
* Wait loop: Take lock on source surface in video memory.
*/
while(1)
{
ddrval = InternalLock(surf_src_lcl, &src_bits, NULL, DDLOCK_READONLY);
if (ddrval == DD_OK)
{
GET_LPDDRAWSURFACE_GBL_MORE(surf_src)->fpNTAlias = (FLATPTR)src_bits;
break; // successfully locked source surface
}
if (ddrval != DDERR_WASSTILLDRAWING)
{
/*
* We can't lock the source surface. Fail the call.
*/
if (dest_lock_taken)
{
InternalUnlock(surf_dest_lcl, NULL, NULL, 0);
}
if (pRgn != (LPRGNDATA)&myRgnBuffer)
{
MemFree(pRgn); // this clip region was malloc'd
}
DONE_EXCLUDE();
DONE_BUSY();
LEAVE_BOTH();
return ddrval;
}
}
src_lock_taken = TRUE;
}
}
/*
* Tell the HEL to perform the blit.
*/
#ifdef WINNT
try_again:
#endif
DOHALCALL_NOWIN16(AlphaBlt, bltfn, bd, rc, sbi.helonly);
#ifdef WINNT
if (rc == DDHAL_DRIVER_HANDLED && bd.ddRVal == DDERR_VISRGNCHANGED)
{
DPF(5,"Resetting VisRgn for surface %x", surf_dest_lcl);
DdResetVisrgn(surf_dest_lcl, (HWND)0);
goto try_again;
}
#endif
}
/*
* If clip region was malloc'd, free it now.
*/
if (pRgn != (LPRGNDATA)&myRgnBuffer)
{
MemFree(pRgn);
}
if(IsD3DManaged(surf_dest_lcl))
MarkDirty(surf_dest_lcl);
DONE_LOCKS();
/*
* Exclusion needs to happen after unlock call
*/
DONE_EXCLUDE();
DONE_BUSY();
LEAVE_BOTH();
return bd.ddRVal;
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPF_ERR("Exception encountered doing alpha blt");
DONE_LOCKS();
DONE_EXCLUDE();
DONE_BUSY();
LEAVE_BOTH();
return DDERR_EXCEPTION;
}
} /* DD_Surface_AlphaBlt */