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.
 
 
 
 
 
 

2691 lines
102 KiB

/******************************Module*Header*******************************\
* Module Name: alphablt.cxx
*
* Alpha Blending
*
* Created: 21-Jun-1996
* Author: Mark Enstrom [marke]
*
* Copyright (c) 1996-1999 Microsoft Corporation
\**************************************************************************/
#include "precomp.hxx"
#include "stretch.hxx" // For mirroring code.
/******************************Public*Routine******************************\
* bIsSourceBGRA
*
* determine whether a surface is in BGR format
*
* Arguments:
*
* pSurf - pointer to the surface
*
* Return Value:
*
* TRUE if the surface is in BGR format, otherwise FALSE
*
* History:
*
* 12-Aug-1997 -by- Ori Gershony [orig]
*
\**************************************************************************/
BOOL
bIsSourceBGRA(
PSURFACE pSurf
)
{
XEPALOBJ pal(pSurf->ppal());
//
// A surface is in BGR format if it's a valid 32BPP surface that's either
// PAL_BGR or PAL_BITFIELDS with the correct bitfields set.
//
return ((pSurf->iFormat() == BMF_32BPP) &&
(pal.bValid()) &&
((pal.bIsBGR()) ||
((pal.bIsBitfields()) &&
(pal.flRed() == 0xff0000) &&
(pal.flGre() == 0xff00) &&
(pal.flBlu() == 0xff))));
}
/******************************Public*Routine******************************\
* psSetupTransparentSrcSurface
*
* make a temp copy of source surface if needed
*
* Arguments:
*
* pSurfSrc - original source surface
* pSurfDst - original dset surfaca
* prclDst - destination rect
* pxloSrcTo32 - used only for alpha blend, tran src to 32 BGRA
* prclSrc - source rect, change to temp src rect if allocated
* &surfTmpSrc - use this surfmem to alloc
* *bAllocSrcSurf - force temp allocation
* ulSourceType - alpha or transparent surface
* ulTranColor - transparent color
*
* Return Value:
*
* drawable surface or NULL
*
* History:
*
* 25-Jun-1996 -by- Mark Enstrom [marke]
*
\**************************************************************************/
PSURFACE
psSetupTransparentSrcSurface(
PSURFACE pSurfSrc,
PSURFACE pSurfDst,
PRECTL prclDst,
XLATEOBJ *pxloSrcTo32,
PRECTL prclSrc,
SURFMEM &surfTmpSrc,
ULONG ulSourceType,
ULONG ulTranColor
)
{
BOOL bStatus;
PSURFACE psurfRet = pSurfSrc;
LONG DstCx = prclDst->right - prclDst->left;
LONG DstCy = prclDst->bottom - prclDst->top;
LONG SrcCx = prclSrc->right - prclSrc->left;
LONG SrcCy = prclSrc->bottom - prclSrc->top;
BOOL bStretch = ((DstCx != SrcCx) || (DstCy != SrcCy));
BOOL bSourceIsBGRA = FALSE;
BOOL bSrcRectExceedsBounds = FALSE;
//
// if the surface is a bitmap, identity translate and
// no stretching, then a temp copy of the surface is not
// needed.
//
if (bStretch)
{
DEVBITMAPINFO dbmi;
PDEVOBJ pdoSrc( pSurfSrc->hdev());
XEPALOBJ palSurf(pSurfSrc->ppal());
//
// calculate clipped extents of destinatoin rect
//
RECTL rclDstClip = {0,0,pSurfDst->sizl().cx,pSurfDst->sizl().cy};
//
// trimmed destination rect is trimmed surface boundary
// Perf Note: clipping on destination is not taken into account,
// this could reduce the size of the source copy surface at
// times.
//
if (rclDstClip.left < prclDst->left)
{
rclDstClip.left = prclDst->left;
}
if (rclDstClip.top < prclDst->top)
{
rclDstClip.top = prclDst->top;
}
if (rclDstClip.right > prclDst->right)
{
rclDstClip.right = prclDst->right;
}
if (rclDstClip.bottom > prclDst->bottom)
{
rclDstClip.bottom = prclDst->bottom;
}
if ((rclDstClip.left < rclDstClip.right) &&
(rclDstClip.top < rclDstClip.bottom))
{
//
// does source rect exceed source bounds? (bad)
//
if ((prclSrc->left < 0) ||
(prclSrc->right > pSurfSrc->sizl().cx) ||
(prclSrc->top < 0) ||
(prclSrc->bottom > pSurfSrc->sizl().cy)
)
{
bSrcRectExceedsBounds = TRUE;
}
//
// allocate surface as same size as dst
//
if (ulSourceType == SOURCE_ALPHA)
{
//
// does original source contain alpha channel
//
bSourceIsBGRA = bIsSourceBGRA (pSurfSrc);
//
// allocate 32bpp BGRA surface for source, must be zero init
//
dbmi.cxBitmap = rclDstClip.right - rclDstClip.left;
dbmi.cyBitmap = rclDstClip.bottom - rclDstClip.top;
dbmi.iFormat = BMF_32BPP;
dbmi.fl = pSurfSrc->bUMPD() ? UMPD_SURFACE : 0;
XEPALOBJ palRGB(gppalRGB);
dbmi.hpal = (HPALETTE)palRGB.hpal();
bStatus = surfTmpSrc.bCreateDIB(&dbmi, (VOID *) NULL);
//
// since DIB is zero-init, no other initialization is needed to
// make it transparent (so that portions of dst rect not covered
// by source rect are not drawn)
//
// UNLESS source DIB does not have it's own alpha. In that case, if
// the source extents do not completely cover dst extents, source
// must be initialized to 0xffxxxxxx. StretchBlt will write 0x00xxxxxx.
// After StretchBlt, must make all 0xffxxxxxx to 0x00xxxxxx and all
// 0x00xxxxxx to 0xffxxxxxx
//
if (bStatus && bSrcRectExceedsBounds && !bSourceIsBGRA)
{
RtlFillMemoryUlong(surfTmpSrc.ps->pvBits(),surfTmpSrc.ps->cjBits(),0xFF000000);
}
}
else
{
//
// allocate compatible surface for TransparentBlt
//
dbmi.cxBitmap = DstCx;
dbmi.cyBitmap = DstCy;
dbmi.iFormat = pSurfSrc->iFormat();
dbmi.fl = pSurfSrc->bUMPD() ? UMPD_SURFACE : 0;
dbmi.hpal = (HPALETTE)NULL;
if (palSurf.bValid())
{
dbmi.hpal = palSurf.hpal();
}
bStatus = surfTmpSrc.bCreateDIB(&dbmi, (VOID *) NULL);
//
// init DIB to transparent
// (so that portions of dst rect not covered by source rect are not drawn)
//
if (bStatus && bSrcRectExceedsBounds)
{
ULONG i;
ULONG cjBits = surfTmpSrc.ps->cjBits();
ULONG ulColor4BPP;
switch (pSurfSrc->iFormat())
{
case BMF_1BPP:
if (ulTranColor)
{
memset(surfTmpSrc.ps->pvBits(),0xff,cjBits);
}
else
{
memset(surfTmpSrc.ps->pvBits(),0,cjBits);
}
break;
case BMF_4BPP:
ulColor4BPP = ulTranColor | (ulTranColor << 4);
memset(surfTmpSrc.ps->pvBits(),ulColor4BPP,cjBits);
break;
case BMF_8BPP:
memset(surfTmpSrc.ps->pvBits(),ulTranColor,cjBits);
break;
case BMF_16BPP:
{
PUSHORT pvBits = (PUSHORT) surfTmpSrc.ps->pvBits();
for (i=0; i<(cjBits/sizeof(USHORT)); i++)
{
*pvBits++ = (USHORT) ulTranColor;
}
}
break;
case BMF_24BPP:
{
BYTE bC1 = ((PBYTE)&ulTranColor)[0];
BYTE bC2 = ((PBYTE)&ulTranColor)[1];
BYTE bC3 = ((PBYTE)&ulTranColor)[2];
PULONG pulDstY = (PULONG)surfTmpSrc.ps->pvScan0();
PULONG pulDstLastY = (PULONG)((PBYTE)pulDstY +
(surfTmpSrc.ps->lDelta() * surfTmpSrc.ps->sizl().cy));
while (pulDstY != pulDstLastY)
{
PBYTE pulDstX = (PBYTE) pulDstY;
PBYTE pulDstLastX = pulDstX + 3 * surfTmpSrc.ps->sizl().cx;
while (pulDstX < pulDstLastX-2)
{
*pulDstX++ = bC1;
*pulDstX++ = bC2;
*pulDstX++ = bC3;
}
pulDstY = (PULONG)((PBYTE)pulDstY + surfTmpSrc.ps->lDelta());
}
}
break;
case BMF_32BPP:
{
PULONG pvBits = (PULONG) surfTmpSrc.ps->pvBits();
for (i=0; i<(cjBits/sizeof(ULONG)); i++)
{
*pvBits++ = ulTranColor;
}
}
break;
}
}
}
if (bStatus)
{
//
// zero DIB to make non-drawing areas transparent for alphablend
//
POINTL ptlBrushOrg = {0,0};
RECTL rclDstCopy = *prclDst;
ECLIPOBJ eco;
ECLIPOBJ *pco = NULL;
RGNMEMOBJTMP rmo((BOOL)FALSE);
if (rmo.bValid())
{
//
// offset dst rect
//
rclDstCopy.left -= rclDstClip.left;
rclDstCopy.right -= rclDstClip.left;
rclDstCopy.top -= rclDstClip.top;
rclDstCopy.bottom -= rclDstClip.top;
//
// will need rect clipping if rclDstCopy exceeds tmp bitmap
//
if (
(rclDstCopy.left < 0) ||
(rclDstCopy.right > surfTmpSrc.ps->sizl().cx) ||
(rclDstCopy.top < 0) ||
(rclDstCopy.bottom > surfTmpSrc.ps->sizl().cy))
{
ERECTL rclSurface(0,0,surfTmpSrc.ps->sizl().cx,surfTmpSrc.ps->sizl().cy);
rmo.vSet((RECTL *) &rclSurface);
pco = (ECLIPOBJ *)&eco;
((XCLIPOBJ *)pco)->vSetup(rmo.prgnGet(),(ERECTL)rclDstCopy);
}
surfTmpSrc.ps->hdev(pSurfSrc->hdev());
//
// init with stretch
//
bStatus = EngStretchBlt (
surfTmpSrc.ps->pSurfobj(),
pSurfSrc->pSurfobj(),
NULL,
(CLIPOBJ *)pco,
pxloSrcTo32,
NULL,
&ptlBrushOrg,
&rclDstCopy,
prclSrc,
NULL,
COLORONCOLOR
);
if (bStatus)
{
//
// adjust prclSrc and prclDst to be non-stretch rects
//
prclSrc->left = 0;
prclSrc->right = dbmi.cxBitmap;
prclSrc->top = 0;
prclSrc->bottom = dbmi.cyBitmap;
*prclDst = rclDstClip;
//
// for alpha bitmaps that did not originally contain an alpha channel,
// init alpha to ff.
//
// PERF: 2 other options to XOR whole bitmap are
// 1: use compatible bitmap for source where rclSrc does not exceed src bounds
// This saves memory maybee, saves xor, but require conversion of each scan to 32BRGA
// 2: use flag to ignore alpha channel later where rclSrc does not exceed src bounds
//
if ((ulSourceType == SOURCE_ALPHA) && (!bSourceIsBGRA))
{
//
// ULONGs that are 0xffxxxxxx must be made 0x00xxxxxx
// ULONGs that are 0x00xxxxxx must be made 0xffxxxxxx
// bitmaps that started out as 0x00BBGGRR (PAL_RGB) are still broken
//
PULONG pulDstY = (PULONG)surfTmpSrc.ps->pvScan0();
PULONG pulDstLastY = (PULONG)((PBYTE)pulDstY + surfTmpSrc.ps->lDelta() * surfTmpSrc.ps->sizl().cy);
while (pulDstY != pulDstLastY)
{
PULONG pulDstX = pulDstY;
PULONG pulDstLastX = pulDstX + surfTmpSrc.ps->sizl().cx;
while (pulDstX != pulDstLastX)
{
*pulDstX = *pulDstX ^ 0xff000000;
pulDstX++;
}
pulDstY = (PULONG)((PBYTE)pulDstY + surfTmpSrc.ps->lDelta());
}
}
//
// mark surface to keep, set return status
//
psurfRet = surfTmpSrc.ps;
}
else
{
psurfRet = NULL;
}
}
else
{
psurfRet = NULL;
}
}
else
{
psurfRet = NULL;
}
}
else
{
psurfRet = NULL;
}
}
else
{
//
// trim src rect to src surface bounds and reduce
// dst rect accordingly
//
if (prclSrc->left < 0)
{
prclDst->left = prclDst->left - prclSrc->left;
prclSrc->left = 0;
}
if (prclSrc->right > pSurfSrc->sizl().cx)
{
prclDst->right = prclDst->right - (prclSrc->right - pSurfSrc->sizl().cx);
prclSrc->right = pSurfSrc->sizl().cx;
}
if (prclSrc->top < 0)
{
prclDst->top = prclDst->top - prclSrc->top;
prclSrc->top = 0;
}
if (prclSrc->bottom > pSurfSrc->sizl().cy)
{
prclDst->bottom = prclDst->bottom - (prclSrc->bottom - pSurfSrc->sizl().cy);
prclSrc->bottom = pSurfSrc->sizl().cy;
}
//
// check dst rect exceeds dst surface, reduce src and rect accordingly
// WINBUG #82938 2-8-2000 bhouse Fix assumption of surface position
// Old Comment:
// This code assumes that the surface starts at 0,0 and ends at sizl().cx, sizl().cy
// which is not true for multimon code.
// This is not a problem anymore. As this is called only from
// EngAlphaBlend. According to Nagasae-San all Eng* Apis can assume
// this surface start of 0,0 requirment.
if (prclDst->left < 0)
{
prclSrc->left += (-prclDst->left);
prclDst->left = 0;
}
if (prclDst->right > pSurfDst->sizl().cx)
{
prclSrc->right += (pSurfDst->sizl().cx - prclDst->right);
prclDst->right = pSurfDst->sizl().cx;
}
if (prclDst->top < 0)
{
prclSrc->top += (-prclDst->top);
prclDst->top = 0;
}
if (prclDst->bottom > pSurfDst->sizl().cy)
{
prclSrc->bottom += (pSurfDst->sizl().cy - prclDst->bottom);
prclDst->bottom = pSurfDst->sizl().cy;
}
//
// check for empty rect
//
if (
(prclDst->left >= prclDst->right) ||
(prclDst->top >= prclDst->bottom))
{
//
// indicate empty rect
//
prclDst->left = prclDst->right;
}
else
{
if (pSurfSrc->iType() != STYPE_BITMAP)
{
DEVBITMAPINFO dbmi;
PDEVOBJ pdoSrc( pSurfSrc->hdev());
XEPALOBJ palSurf(pSurfSrc->ppal());
LONG DstCx = prclDst->right - prclDst->left;
LONG DstCy = prclDst->bottom - prclDst->top;
//
// allocate surface as same size as dst
//
dbmi.cxBitmap = DstCx;
dbmi.cyBitmap = DstCy;
dbmi.iFormat = pSurfSrc->iFormat();
dbmi.fl = pSurfSrc->bUMPD() ? UMPD_SURFACE : 0;
dbmi.hpal = (HPALETTE)NULL;
if (palSurf.bValid())
{
dbmi.hpal = palSurf.hpal();
}
bStatus = surfTmpSrc.bCreateDIB(&dbmi, (VOID *) NULL);
if (bStatus)
{
RECTL rclCopy;
rclCopy.left = 0;
rclCopy.right = DstCx;
rclCopy.top = 0;
rclCopy.bottom = DstCy;
surfTmpSrc.ps->hdev(pSurfSrc->hdev());
//
// if src is same size as dest, init with CopyBits
//
POINTL ptlCopy;
ptlCopy.x = prclSrc->left;
ptlCopy.y = prclSrc->top;
(*PPFNGET(pdoSrc,CopyBits,pSurfSrc->flags()))(
surfTmpSrc.ps->pSurfobj(),
pSurfSrc->pSurfobj(),
(CLIPOBJ *) NULL,
(XLATEOBJ *)NULL,
&rclCopy,
&ptlCopy);
//
// adjust prclSrc to be the new rect
//
*prclSrc = rclCopy;
//
// mark surface to keep, set return status
//
psurfRet = surfTmpSrc.ps;
}
else
{
psurfRet = NULL;
}
}
}
}
return(psurfRet);
}
/******************************Public*Routine******************************\
* psSetupDstSurface
*
* Create temporary destination surface for alpha and gradient fill,
* if necessary, and optionally copy bits from the
* actual destination surface.
*
* Arguments:
*
* pSurfDst - actual destination surface
* prclDst - rectangle on dest surface
* surfTmpDst - reference to surfmem
* bForceDstAlloc - force allocation of temp dest
* bCopyFromDst - copy bits from actual destination surface
*
* Return Value:
*
* surface to use, either original or new
*
* History:
*
* 25-Jun-1996 -by- Mark Enstrom [marke]
*
\**************************************************************************/
PSURFACE
psSetupDstSurface(
PSURFACE pSurfDst,
PRECTL prclDst,
SURFMEM &surfTmpDst,
BOOL bForceDstAlloc,
BOOL bCopyFromDst
)
{
PSURFACE psurfRet = pSurfDst;
LONG DstCx = prclDst->right - prclDst->left;
LONG DstCy = prclDst->bottom - prclDst->top;
BOOL bStatus = FALSE;
if (bForceDstAlloc || (pSurfDst->iType() != STYPE_BITMAP))
{
DEVBITMAPINFO dbmi;
PDEVOBJ pdoDst( pSurfDst->hdev());
XEPALOBJ palSurf(pSurfDst->ppal());
//
// allocate surface
//
dbmi.cxBitmap = DstCx;
dbmi.cyBitmap = DstCy;
dbmi.iFormat = pSurfDst->iFormat();
dbmi.fl = pSurfDst->bUMPD() ? UMPD_SURFACE : 0;
dbmi.hpal = (HPALETTE) 0;
if (palSurf.bValid())
{
dbmi.hpal = palSurf.hpal();
}
bStatus = surfTmpDst.bCreateDIB(&dbmi, (VOID *) NULL);
if (bStatus)
{
RECTL rclCopy;
POINTL ptlCopy;
surfTmpDst.ps->hdev(pSurfDst->hdev());
rclCopy.left = 0;
rclCopy.right = DstCx;
rclCopy.top = 0;
rclCopy.bottom = DstCy;
if (bCopyFromDst)
{
ptlCopy.x = prclDst->left;
ptlCopy.y = prclDst->top;
bStatus = (*PPFNGET(pdoDst,CopyBits,pSurfDst->flags()))(
surfTmpDst.pSurfobj(),
pSurfDst->pSurfobj(),
(CLIPOBJ *) NULL,
&xloIdent,
&rclCopy,
&ptlCopy);
}
if (bStatus)
{
//
// adjust dst rect
//
*prclDst = rclCopy;
psurfRet = surfTmpDst.ps;
}
else
{
psurfRet = NULL;
}
}
else
{
psurfRet = NULL;
}
}
return(psurfRet);
}
#if defined(_X86_)
/**************************************************************************\
* IsMMXProcessor
*
* determine if the processor supports MMX
*
* Arguments:
*
* none
*
* Return Value:
*
* TRUE if MMX
*
* History:
*
* 4/10/1997 Mark Enstrom [marke]
*
\**************************************************************************/
BOOL
bIsMMXProcessor(VOID)
{
BOOL retval = FALSE;
#if 0
PVOID pFloatingPointState = (PVOID)PALLOCMEM(sizeof(KFLOATING_SAVE) + sizeof(BOOL),'pftG');
if (pFloatingPointState != NULL)
{
BOOL bRet = EngSaveFloatingPointState(pFloatingPointState,sizeof(KFLOATING_SAVE) + sizeof(BOOL));
if (bRet)
{
__try
{
_asm emms
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
retval = FALSE;
}
EngRestoreFloatingPointState(pFloatingPointState);
}
else
{
WARNING1("bIsMMXProcessor: Failed to save fp state\n");
retval = FALSE;
}
VFREEMEM(pFloatingPointState);
}
else
{
WARNING1("bIsMMXProcessor: Failed to allocate fpstate\n");
retval = FALSE;
}
#else
if (ExIsProcessorFeaturePresent(3)) // PF_MMX_INSTRUCTION_AVAILABLE
{
retval = TRUE;
}
#endif
return retval;
}
#endif
/**************************************************************************\
* bDetermineAlphaBlendFunction
*
* determine alpha blending routine based on src and dst formats
* and alpha BlendFunction
*
* Arguments:
*
* pSurfDst - dest surface
* pSurfSrc - src surface
* ppalDst - dest palette
* ppalSrc - src palette
* cxDst - width of alpha blt
* pAlphaDispatch - blend function and routines
*
* Return Value:
*
* status
*
* History:
*
* 1/21/1997 Mark Enstrom [marke]
*
\**************************************************************************/
BOOL
bDetermineAlphaBlendFunction(
PSURFACE pSurfDst,
PSURFACE pSurfSrc,
XEPALOBJ *ppalDst,
XEPALOBJ *ppalSrc,
XLATE *pxlateSrcTo32,
LONG cxDst,
PALPHA_DISPATCH_FORMAT pAlphaDispatch
)
{
BOOL bRet = TRUE;
BOOL bSrcHasAlpha = FALSE;
pAlphaDispatch->bUseMMX = FALSE;
//
// does src bitmap have alpha
//
bSrcHasAlpha = (pAlphaDispatch->BlendFunction.AlphaFormat & AC_SRC_ALPHA);
//
// assume default blend, check for special cases
//
pAlphaDispatch->pfnGeneralBlend = vAlphaPerPixelOnly;
//
// use "over" optimized blend fucntion
//
if (bSrcHasAlpha && (pAlphaDispatch->BlendFunction.SourceConstantAlpha == 255))
{
pAlphaDispatch->pfnGeneralBlend = vAlphaPerPixelOnly;
#if defined(_X86_)
//
// source and dest alignment must be 8 byte aligned to use mmx
//
if (gbMMXProcessor && (cxDst >= 8))
{
pAlphaDispatch->pfnGeneralBlend = mmxAlphaPerPixelOnly;
pAlphaDispatch->bUseMMX = TRUE;
}
#endif
}
else
{
//
// if source format doesn't support alpha then use
// constant alpha routine
//
if (bSrcHasAlpha)
{
//
// blend source and dest using SourceAlpha and
// source bitmaps integral alpha
//
pAlphaDispatch->pfnGeneralBlend = vAlphaPerPixelAndConst;
#if defined(_X86_)
//
// source and dest alignment must be 8 byte aligned to use mmx
//
if (gbMMXProcessor && (cxDst >= 8))
{
pAlphaDispatch->pfnGeneralBlend = mmxAlphaPerPixelAndConst;
pAlphaDispatch->bUseMMX = TRUE;
}
#endif
}
else
{
//
// blend src and dest using SourceConstantAlpha.
//
pAlphaDispatch->pfnGeneralBlend = vAlphaConstOnly;
}
}
//
// determine output conversion and storage routines
//
switch (pSurfDst->iFormat())
{
case BMF_1BPP:
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvert1ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRATo1;
pAlphaDispatch->ulDstBitsPerPixel = 1;
break;
case BMF_4BPP:
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvert4ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRATo4;
pAlphaDispatch->ulDstBitsPerPixel = 4;
break;
case BMF_8BPP:
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvert8ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRATo8;
pAlphaDispatch->ulDstBitsPerPixel = 8;
break;
case BMF_16BPP:
ASSERTGDI((ppalDst->bIsBitfields()),"AlphaBlt: RGB16 palette must be bitfields");
if (
(ppalDst->flRed() == 0xf800) &&
(ppalDst->flGre() == 0x07e0) &&
(ppalDst->flBlu() == 0x001f)
)
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvertRGB16_565ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRAToRGB16_565;
}
else if (
(ppalDst->flRed() == 0x7c00) &&
(ppalDst->flGre() == 0x03e0) &&
(ppalDst->flBlu() == 0x001f)
)
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvertRGB16_555ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRAToRGB16_555;
}
else
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvert16BitfieldsToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRAToRGB16Bitfields;
}
pAlphaDispatch->ulDstBitsPerPixel = 16;
break;
case BMF_24BPP:
// WINBUG #101656 bhouse 5-4-2000 AlphaBlend reversing R and B channels
// when rendering to 24BPP
if (
(ppalDst->bIsBGR()) ||
(
(ppalDst->bIsBitfields()) &&
(
(
(ppalDst->flRed() == 0xff0000) &&
(ppalDst->flGre() == 0x00ff00) &&
(ppalDst->flBlu() == 0x0000ff)
) ||
(
(ppalDst->flRed() == 0x000000) &&
(ppalDst->flGre() == 0x000000) &&
(ppalDst->flBlu() == 0x000000)
)
)
)
)
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvertBGR24ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRAToBGR24;
pAlphaDispatch->ulDstBitsPerPixel = 24;
}
else
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvertRGB24ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRAToRGB24;
pAlphaDispatch->ulDstBitsPerPixel = 24;
}
break;
case BMF_32BPP:
if (
(ppalDst->bIsBGR()) ||
(
(ppalDst->bIsBitfields()) &&
(
(
(ppalDst->flRed() == 0xff0000) &&
(ppalDst->flGre() == 0x00ff00) &&
(ppalDst->flBlu() == 0x0000ff)
) ||
(
(ppalDst->flRed() == 0x000000) &&
(ppalDst->flGre() == 0x000000) &&
(ppalDst->flBlu() == 0x000000)
)
)
)
)
{
//
// assigned to null indicates no conversion needed
//
pAlphaDispatch->pfnLoadDstAndConvert = NULL;
pAlphaDispatch->pfnConvertAndStore = NULL;
}
else if (ppalDst->flPal() & PAL_RGB)
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvertRGB32ToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRAToRGB32;
}
else
{
pAlphaDispatch->pfnLoadDstAndConvert = vLoadAndConvert32BitfieldsToBGRA;
pAlphaDispatch->pfnConvertAndStore = vConvertAndSaveBGRATo32Bitfields;
}
pAlphaDispatch->ulDstBitsPerPixel = 32;
break;
default:
WARNING("bDetermineAlphaBlendFunction: Illegal bitmap format\n");
bRet = FALSE;
}
//
// determine input load and conversion routine
//
switch (pSurfSrc->iFormat())
{
case BMF_1BPP:
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvert1ToBGRA;
pAlphaDispatch->ulSrcBitsPerPixel = 1;
break;
case BMF_4BPP:
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvert4ToBGRA;
pAlphaDispatch->ulSrcBitsPerPixel = 4;
break;
case BMF_8BPP:
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvert8ToBGRA;
pAlphaDispatch->ulSrcBitsPerPixel = 8;
break;
case BMF_16BPP:
ASSERTGDI((ppalSrc->bIsBitfields()),"AlphaBlt: RGB16 palette must be bitfields");
if ((ppalSrc->flRed() == 0xf800) &&
(ppalSrc->flGre() == 0x07e0) &&
(ppalSrc->flBlu() == 0x001f))
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvertRGB16_565ToBGRA;
}
else if ((ppalSrc->flRed() == 0x7c00) &&
(ppalSrc->flGre() == 0x03e0) &&
(ppalSrc->flBlu() == 0x001f))
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvertRGB16_555ToBGRA;
}
else
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvert16BitfieldsToBGRA;
}
pAlphaDispatch->ulSrcBitsPerPixel = 16;
break;
case BMF_24BPP:
// WINBUG #101656 bhouse 5-4-2000 AlphaBlend reversing R and B channels
// when rendering to 24BPP
if ((ppalSrc->bIsBGR()) ||
(
(ppalSrc->bIsBitfields()) &&
(
(
(ppalSrc->flRed() == 0xff0000) &&
(ppalSrc->flGre() == 0x00ff00) &&
(ppalSrc->flBlu() == 0x0000ff)
) ||
(
(ppalSrc->flRed() == 0x000000) &&
(ppalSrc->flGre() == 0x000000) &&
(ppalSrc->flBlu() == 0x000000)
)
)
)
)
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvertBGR24ToBGRA;
}
else
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvertRGB24ToBGRA;
}
pAlphaDispatch->ulSrcBitsPerPixel = 24;
break;
case BMF_32BPP:
if (
(pxlateSrcTo32 == NULL) ||
(ppalSrc->bIsBGR()) ||
(
(ppalSrc->bIsBitfields()) &&
(
(
(ppalSrc->flRed() == 0xff0000) &&
(ppalSrc->flGre() == 0x00ff00) &&
(ppalSrc->flBlu() == 0x0000ff)
) ||
(
(ppalSrc->flRed() == 0x000000) &&
(ppalSrc->flGre() == 0x000000) &&
(ppalSrc->flBlu() == 0x000000)
)
)
)
)
{
pAlphaDispatch->pfnLoadSrcAndConvert = NULL;
}
else if (ppalSrc->flPal() & PAL_RGB)
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvertRGB32ToBGRA;
}
else
{
pAlphaDispatch->pfnLoadSrcAndConvert = vLoadAndConvert32BitfieldsToBGRA;
}
pAlphaDispatch->ulSrcBitsPerPixel = 32;
break;
default:
WARNING("bDetermineAlphaBlendFunction: Illegal bitmap format\n");
bRet = FALSE;
}
//
// 16/24 bit per pixel blend optimization
//
if (pAlphaDispatch->pfnGeneralBlend == vAlphaConstOnly)
{
if ((pAlphaDispatch->pfnLoadSrcAndConvert == vLoadAndConvertRGB16_555ToBGRA) &&
(pAlphaDispatch->pfnLoadDstAndConvert == vLoadAndConvertRGB16_555ToBGRA))
{
//
// use direct 16 bpp blend
//
pAlphaDispatch->pfnGeneralBlend = vAlphaConstOnly16_555;
#if defined(_X86_)
if (gbMMXProcessor && (cxDst >= 8))
{
pAlphaDispatch->pfnGeneralBlend = mmxAlphaConstOnly16_555;
pAlphaDispatch->bUseMMX = TRUE;
}
#endif
pAlphaDispatch->pfnLoadSrcAndConvert = NULL;
pAlphaDispatch->pfnLoadDstAndConvert = NULL;
pAlphaDispatch->pfnConvertAndStore = NULL;
//
// convert blend function from x/255 to y/31
//
int ia = pAlphaDispatch->BlendFunction.SourceConstantAlpha;
ia = (ia * 31 + 128)/255;
pAlphaDispatch->BlendFunction.SourceConstantAlpha = (BYTE)ia;
}
else if ((pAlphaDispatch->pfnLoadSrcAndConvert == vLoadAndConvertRGB16_565ToBGRA) &&
(pAlphaDispatch->pfnLoadDstAndConvert == vLoadAndConvertRGB16_565ToBGRA))
{
//
// use direct 16 bpp blend
//
pAlphaDispatch->pfnGeneralBlend = vAlphaConstOnly16_565;
#if defined(_X86_)
if (gbMMXProcessor && (cxDst >= 8))
{
pAlphaDispatch->pfnGeneralBlend = mmxAlphaConstOnly16_565;
pAlphaDispatch->bUseMMX = TRUE;
}
#endif
pAlphaDispatch->pfnLoadSrcAndConvert = NULL;
pAlphaDispatch->pfnLoadDstAndConvert = NULL;
pAlphaDispatch->pfnConvertAndStore = NULL;
//
// convert blend function from x/255 to y/31
//
int ia = pAlphaDispatch->BlendFunction.SourceConstantAlpha;
ia = (ia * 31 + 128)/255;
pAlphaDispatch->BlendFunction.SourceConstantAlpha = (BYTE)ia;
}
else if ((pAlphaDispatch->pfnLoadSrcAndConvert == vLoadAndConvertRGB24ToBGRA) &&
(pAlphaDispatch->pfnLoadDstAndConvert == vLoadAndConvertRGB24ToBGRA))
{
//
// use direct 24 bpp blend
//
pAlphaDispatch->pfnGeneralBlend = vAlphaConstOnly24;
#if defined(_X86_)
if (gbMMXProcessor && (cxDst >= 8))
{
pAlphaDispatch->pfnGeneralBlend = mmxAlphaConstOnly24;
pAlphaDispatch->bUseMMX = TRUE;
}
#endif
pAlphaDispatch->pfnLoadSrcAndConvert = NULL;
pAlphaDispatch->pfnLoadDstAndConvert = NULL;
pAlphaDispatch->pfnConvertAndStore = NULL;
}
}
return (bRet);
}
/******************************Public*Routine******************************\
* EngAlphaBlend
*
* Implement alpha blending
*
* Arguments:
*
* psoDst - destination surface
* psoSrc - source surface
* pco - clip object
* pxloSrcTo32 - translate src to 32 bgr
* pxloDstTo32 - translate dst to 32 bgr
* pxlo32ToDst - translate 32bgr to dst
* prclDst - dest rect
* prclSrc - src rect
* BlendFunction - blend function
*
* Return Value:
*
* status
*
* History:
*
* 21-Jun-1996 -by- Mark Enstrom [marke]
*
\**************************************************************************/
BOOL
EngAlphaBlend(
SURFOBJ *psoDst,
SURFOBJ *psoSrc,
CLIPOBJ *pco,
XLATEOBJ *pxlo,
RECTL *prclDst,
RECTL *prclSrc,
BLENDOBJ *pBlendObj
)
{
BOOL bRet = TRUE;
ASSERTGDI((prclSrc->left < prclSrc->right) &&
(prclSrc->top < prclSrc->bottom) &&
(prclDst->left < prclDst->right) &&
(prclDst->top < prclDst->bottom),
"Invalid rectangles");
EBLENDOBJ *peBlendObj = (EBLENDOBJ*)pBlendObj;
PSURFACE pSurfDst = SURFOBJ_TO_SURFACE(psoDst);
PSURFACE pSurfSrc = SURFOBJ_TO_SURFACE(psoSrc);
PSURFACE pSurfSrcTmp;
PSURFACE pSurfDstTmp;
BOOL bAllocDstSurf = FALSE;
XLATE *pxlateSrcTo32 = (XLATE *)peBlendObj->pxloSrcTo32;
XLATE *pxlateDstTo32 = (XLATE *)peBlendObj->pxloDstTo32;
XLATE *pxlate32ToDst = (XLATE *)peBlendObj->pxlo32ToDst;
RECTL rclDstWk = *prclDst;
RECTL rclSrcWk = *prclSrc;
POINTL ptlSrc;
CLIPOBJ *pcoDstWk = pco;
ALPHA_DISPATCH_FORMAT AlphaDispatch;
//
// check blend
//
AlphaDispatch.BlendFunction = peBlendObj->BlendFunction;
ASSERTGDI(peBlendObj->BlendFunction.BlendOp == AC_SRC_OVER,
"Invalid blend");
//
// must be alpha to pull palette information from xlates
//
SURFMEM surfTmpDst;
SURFMEM surfTmpSrc;
//
// For profiling purposes, set a flag in the PDEV to indicate that the
// driver punted this call.
//
{
PDEVOBJ po(pSurfDst->hdev());
if (po.bValid())
{
po.vDriverPuntedCall(TRUE);
}
}
if(peBlendObj->BlendFunction.BlendFlags & AC_USE_HIGHQUALITYFILTER)
{
BOOL bStretch = FALSE;
bStretch = ((rclDstWk.right - rclDstWk.left) != (rclSrcWk.right - rclSrcWk.left)) ||
((rclDstWk.bottom - rclDstWk.top) != (rclSrcWk.bottom - rclSrcWk.top));
// Call EngHTBlt only when we have to stretch. When we dont have to
// stretch we fall through to the old 1:1 EngAlphaBlend code.
if(bStretch)
return EngHTBlt(psoDst,
psoSrc,
NULL,
pco,
pxlo,
NULL,
&gptlZero,
prclDst,
prclSrc,
NULL,
BBPF_DO_ALPHA_BLEND,
pBlendObj) == HTBLT_SUCCESS ? TRUE : FALSE ;
}
//
// Synchronize with the device driver before touching the device surface.
//
{
PDEVOBJ poDst(psoDst->hdev);
poDst.vSync(psoDst, NULL, 0);
PDEVOBJ poSrc(psoSrc->hdev);
poSrc.vSync(psoSrc, NULL, 0);
}
//
// Get a readable source surface that is stretched to the destination size.
//
pSurfSrcTmp = psSetupTransparentSrcSurface(
pSurfSrc,
pSurfDst,
&rclDstWk,
(XLATEOBJ *)pxlateSrcTo32,
&rclSrcWk,
surfTmpSrc,
SOURCE_ALPHA,
0);
if ((pSurfSrcTmp != NULL) && (rclDstWk.left != rclDstWk.right))
{
//
// save final reduced dst rect. psSetupDstSurface may change rclDstWk to
// to a temp dib rect.
//
RECTL rclDstSurface = rclDstWk;
//
// temp surface rect is now same size as dst rect, just need point offset
//
ptlSrc.x = rclSrcWk.left;
ptlSrc.y = rclSrcWk.top;
//
// get a dst surface that can be written to, remember since it may have to
// be written back
//
pSurfDstTmp = psSetupDstSurface(
pSurfDst,
&rclDstWk,
surfTmpDst,
FALSE,
TRUE);
if (pSurfDstTmp)
{
if (pSurfDstTmp != pSurfDst)
{
bAllocDstSurf = TRUE;
}
XEPALOBJ palSrc(pSurfSrcTmp->ppal());
XEPALOBJ palDst(pSurfDstTmp->ppal());
//
// must have one valid surface palette. Must pass drawing routines
// two good palettes so duplicate good pointer
//
if (!palSrc.bValid())
{
//
// get source surface palette
//
// first try the palette translate, because the sprite code
// doesn't want to stick the palette in the surface
//
XLATE* pxlate = (XLATE*) pxlo;
if ((pxlate != NULL) && (pxlate->ppalSrc != NULL))
{
palSrc.ppalSet(pxlate->ppalSrc);
}
else
{
PDEVOBJ pdo(pSurfSrcTmp->hdev());
palSrc.ppalSet(pdo.ppalSurf());
}
ASSERTGDI(palSrc.bValid(),"EngAlphaBlend:can't get source palette");
}
if (!palDst.bValid())
{
//
// get destination surface palette
//
PDEVOBJ pdo(pSurfDstTmp->hdev());
ASSERTGDI(pdo.bValid(),"EngAlphaBlend:can't get destination palette");
palDst.ppalSet(pdo.ppalSurf());
}
//
// validate palettes
//
if (palSrc.bValid() && palDst.bValid())
{
LONG cxDst = rclDstWk.right - rclDstWk.left;
//
// if using a temp dst, no need to clip except to bounding rect
//
if (bAllocDstSurf)
{
pcoDstWk = NULL;
}
//
// determine blend function
//
bRet = bDetermineAlphaBlendFunction(pSurfDstTmp,
pSurfSrcTmp,
&palDst,
&palSrc,
pxlateSrcTo32,
cxDst,
&AlphaDispatch);
//
// NOTE:
// May be able to move setup of expensive EXLATE to and from 32 to
// here only for case where non-direct blending is needed
//
if (bRet)
{
KFLOATING_SAVE fsFpState;
//
// if alpha routines use MMX, must save and restore floating
// point state
//
if (bRet && AlphaDispatch.bUseMMX)
{
NTSTATUS status = KeSaveFloatingPointState(&fsFpState);
ASSERTGDI(NT_SUCCESS(status),
"Unexpected KeSaveFloatingPointState failure");
}
//
// Determine the clipping region complexity.
//
CLIPENUMRECT clenr;
BOOL bMore;
ULONG ircl;
//
// default (pcoDstWk = NULL) is use Dst rect as single clip rect,
// same as DC_TRIVIAL
//
bMore = FALSE;
clenr.c = 1;
clenr.arcl[0] = rclDstWk;
if (pcoDstWk != (CLIPOBJ *) NULL)
{
switch(pcoDstWk->iDComplexity)
{
case DC_TRIVIAL:
break;
case DC_RECT:
bMore = FALSE;
clenr.c = 1;
clenr.arcl[0] = pcoDstWk->rclBounds;
break;
case DC_COMPLEX:
bMore = TRUE;
((ECLIPOBJ *) pcoDstWk)->cEnumStart(FALSE,
CT_RECTANGLES,
CD_LEFTDOWN,
CLIPOBJ_ENUM_LIMIT);
break;
default:
RIP("ERROR EngCopyBits bad clipping type");
}
}
//
// run through clipping enum
//
do
{
if (bMore)
{
bMore = ((ECLIPOBJ *) pcoDstWk)->bEnum(sizeof(clenr),
(PVOID) &clenr);
}
for (ircl = 0; ircl < clenr.c; ircl++)
{
PRECTL prcl = &clenr.arcl[ircl];
//
// Insersect the clip rectangle with the target rectangle to
// determine visible recangle
//
if (prcl->left < rclDstWk.left)
{
prcl->left = rclDstWk.left;
}
if (prcl->right > rclDstWk.right)
{
prcl->right = rclDstWk.right;
}
if (prcl->top < rclDstWk.top)
{
prcl->top = rclDstWk.top;
}
if (prcl->bottom > rclDstWk.bottom)
{
prcl->bottom = rclDstWk.bottom;
}
//
// Process the result if it's a valid rectangle.
//
if ((prcl->top < prcl->bottom) && (prcl->left < prcl->right))
{
POINTL pptlSrcOffset;
//
// Figure out the upper-left coordinates of rects to blt.
// NOTE: does not do right->left or bottom->top
//
pptlSrcOffset.x = ptlSrc.x + prcl->left - rclDstWk.left;
pptlSrcOffset.y = ptlSrc.y + prcl->top - rclDstWk.top;
bRet = AlphaScanLineBlend(
(PBYTE)pSurfDstTmp->pvScan0(),
prcl,
pSurfDstTmp->lDelta(),
(PBYTE)pSurfSrcTmp->pvScan0(),
pSurfSrcTmp->lDelta(),
&pptlSrcOffset,
pxlateSrcTo32,
pxlateDstTo32,
pxlate32ToDst,
palDst,
palSrc,
&AlphaDispatch
);
}
}
} while (bMore);
//
// if there is a dst temp surface, need to blt it to
// dst, then free
//
if (bAllocDstSurf)
{
PDEVOBJ pdoDst(pSurfDst->hdev());
POINTL ptlCopy = {0,0};
(*PPFNGET(pdoDst,CopyBits,pSurfDst->flags()))(
pSurfDst->pSurfobj(),
pSurfDstTmp->pSurfobj(),
pco,
&xloIdent,
&rclDstSurface,
&ptlCopy);
}
//
// restore fp state if MMX used
//
if (AlphaDispatch.bUseMMX)
{
KeRestoreFloatingPointState(&fsFpState);
}
}
}
else
{
bRet = FALSE;
}
}
else
{
WARNING1("EngAlphaBlend: failed to allocate and copy surface\n");
bRet = FALSE;
}
}
return (bRet);
}
/******************************Public*Routine******************************\
* NtGdiAlphaBlend
*
* Kernel stub for alpha blending
*
* Arguments:
*
* hdcDst - dst dc
* DstX - dst x origin
* DstY - dst y origin
* DstCx - dst width
* DstCy - dst height
* hdcSrc - src dc
* SrcX - src x origin
* SrcY - src y origin
* SrcCx - src width
* SrcCy - src height
* BlendFunction - blend function
*
* Return Value:
*
* status
*
* History:
*
* 27-Jun-1997 Added rotation support -by- Ori Gershony [orig]
*
* 21-Jun-1996 -by- Mark Enstrom [marke]
*
\**************************************************************************/
BOOL
NtGdiAlphaBlend(
HDC hdcDst,
LONG DstX,
LONG DstY,
LONG DstCx,
LONG DstCy,
HDC hdcSrc,
LONG SrcX,
LONG SrcY,
LONG SrcCx,
LONG SrcCy,
BLENDFUNCTION BlendFunction,
HANDLE hcmXform
)
{
BOOL bStatus = TRUE;
BOOL bQuickStretch = FALSE;
//
// check blend, only support AC_SRC_OVER now
//
if ((BlendFunction.BlendOp != AC_SRC_OVER) ||
((BlendFunction.AlphaFormat & (~ AC_SRC_ALPHA)) != 0))
{
WARNING1("NtGdiAlphaBlend: invalid blend function\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
if ((SrcCx == 0) || (SrcCy == 0))
{
//
// Src rectangle is empty--nothing to do.
//
return TRUE;
}
//
// no mirroring
//
if ((DstCx < 0) || (DstCy < 0) || (SrcCx < 0) || (SrcCy < 0))
{
WARNING1("NtGdiAlphaBlend: mirroring not allowed\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
BOOL bMirrorBitmap = (BlendFunction.BlendFlags & AC_MIRRORBITMAP);
BlendFunction.BlendFlags &= ~AC_MIRRORBITMAP;
//
// validate dst DC
//
DCOBJ dcoDst(hdcDst);
if (!(dcoDst.bValid()) || dcoDst.bStockBitmap())
{
WARNING1("NtGdiAlphaBlend failed: invalid dst DC");
EngSetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
DCOBJ dcoSrc(hdcSrc);
if (dcoSrc.bValid())
{
EXFORMOBJ xoDst(dcoDst, WORLD_TO_DEVICE);
EXFORMOBJ xoSrc(dcoSrc, WORLD_TO_DEVICE);
//
// no source rotation
//
if (!xoSrc.bRotationOrMirroring())
{
//
// Return null operations. Don't need to check source for
// empty because the xforms are the same except translation.
//
ERECTL erclSrc(SrcX,SrcY,SrcX+SrcCx,SrcY+SrcCy);
xoSrc.bXform(erclSrc);
erclSrc.vOrder();
//
// If destination has a rotation, compute a bounding box for the
// resulting parallelogram
//
POINTFIX pptfxDst[4];
ERECTL erclDst;
BOOL bRotationDst;
if ((bRotationDst = xoDst.bRotationOrMirroring()))
{
//
// Compute the resulting parallelogram. In order to make sure we don't lose
// precision in the rotation, we will store the output of the transformation
// in fixed point numbers (this is how PlgBlt does it and we want our output
// to match).
//
POINTL pptlDst[3];
pptlDst[0].x = DstX;
pptlDst[0].y = DstY;
pptlDst[1].x = DstX+DstCx;
pptlDst[1].y = DstY;
pptlDst[2].x = DstX;
pptlDst[2].y = DstY+DstCy;
xoDst.bXform(pptlDst, pptfxDst, 3);
if (!xoDst.bRotation())
{
//
// Mirroring transforms hack: back in windows 3.1, they used to shift
// by one for mirroring transforms. We need to support this here to
// be compatible with NT's BitBlt/StretchBlt that also use this hack, and
// also to be compatible with AlphaBlend that calls BitBlt/StretchBlt
// code when constant alpha=255 and there's no per-pixel alpha. Ick!
// See BLTRECORD::vOrderStupid for details. Also see bug 319917.
//
if (pptfxDst[0].x > pptfxDst[1].x)
{
//
// Mirroring in x
//
pptfxDst[0].x += LTOFX(1);
pptfxDst[1].x += LTOFX(1);
pptfxDst[2].x += LTOFX(1);
}
if (pptfxDst[0].y > pptfxDst[2].y)
{
//
// Mirroring in y
//
pptfxDst[0].y += LTOFX(1);
pptfxDst[1].y += LTOFX(1);
pptfxDst[2].y += LTOFX(1);
}
}
//
// Compute the fourth point using the first three points.
//
pptfxDst[3].x = pptfxDst[1].x + pptfxDst[2].x - pptfxDst[0].x;
pptfxDst[3].y = pptfxDst[1].y + pptfxDst[2].y - pptfxDst[0].y;
//
// Compute the bounding box. Algorithm borrowed from Donald Sidoroff's code
// in EngPlgBlt. Basically the first two statements decide whether the indices of
// the extremas are odd or even, and the last two statements determine exactly what
// they are.
//
int iLeft = (pptfxDst[1].x > pptfxDst[0].x) == (pptfxDst[1].x > pptfxDst[3].x);
int iTop = (pptfxDst[1].y > pptfxDst[0].y) == (pptfxDst[1].y > pptfxDst[3].y);
if (pptfxDst[iLeft].x > pptfxDst[iLeft ^ 3].x)
{
iLeft ^= 3;
}
if (pptfxDst[iTop].y > pptfxDst[iTop ^ 3].y)
{
iTop ^= 3;
}
erclDst = ERECTL(LONG_CEIL_OF_FIX(pptfxDst[iLeft ].x),
LONG_CEIL_OF_FIX(pptfxDst[iTop ].y),
LONG_CEIL_OF_FIX(pptfxDst[iLeft^3].x),
LONG_CEIL_OF_FIX(pptfxDst[iTop^3 ].y));
//
// The vertices should now be in vOrder, but it doesn't hurt to verify this...
//
ASSERTGDI((erclDst.right >= erclDst.left), "NtGdiAlphaBlend: erclDst not in vOrder");
ASSERTGDI((erclDst.bottom >= erclDst.top), "NtGdiAlphaBlend: erclDst not in vOrder");
}
else
{
//
// No rotation--just apply the transformation to the rectangle
//
erclDst = ERECTL(DstX,DstY,DstX+DstCx,DstY+DstCy);
xoDst.bXform(erclDst);
erclDst.vOrder();
}
if (!erclDst.bEmpty())
{
//
// Accumulate bounds. We can do this outside the DEVLOCK
//
if (dcoDst.fjAccum())
{
dcoDst.vAccumulate(erclDst);
}
//
// Lock the Rao region and the surface if we are drawing on a
// display surface. Bail out if we are in full screen mode.
//
DEVLOCKBLTOBJ dlo;
BOOL bLocked;
bLocked = dlo.bLock(dcoDst, dcoSrc);
if (bLocked)
{
//
// Check pSurfDst, this may be an info DC or a memory DC with default bitmap.
//
SURFACE *pSurfDst;
if ((pSurfDst = dcoDst.pSurface()) != NULL)
{
XEPALOBJ palDst(pSurfDst->ppal());
XEPALOBJ palDstDC(dcoDst.ppal());
SURFACE *pSurfSrc = dcoSrc.pSurface();
//
// Basically we check that pSurfSrc is not NULL which
// happens for memory bitmaps with the default bitmap
// and for info DC's. Otherwise we continue if
// the source is readable or if it isn't we continue
// if we are blting display to display or if User says
// we have ScreenAccess on this display DC. Note
// that if pSurfSrc is not readable the only way we
// can continue the blt is if the src is a display.
//
if (pSurfSrc != NULL)
{
if ((pSurfSrc->bReadable()) ||
( (dcoSrc.bDisplay()) &&
((dcoDst.bDisplay()) || UserScreenAccessCheck() )))
{
// Make sure that if the user claims that the source contains an
// Alpha channel, it's a 32BPP source. This is important, because
// the driver has no way of checking whether the source is 32BPP.
if ((BlendFunction.AlphaFormat & AC_SRC_ALPHA) &&
(!(bIsSourceBGRA(pSurfSrc))))
{
WARNING("NtGdiAlphaBlend: AlphaFormat claims that there is an Alpha channel in a surface that's not 32BPP");
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// With a fixed DC origin we can change the destination to SCREEN coordinates.
//
//
// This is useful later for rotations
//
ERECTL erclDstOrig = erclDst;
erclDst += dcoDst.eptlOrigin();
erclSrc += dcoSrc.eptlOrigin();
//
// Make sure the source rectangle lies completely within the source
// surface.
//
BOOL bBadRects;
// If the source is a Meta device, we must check bounds taking its
// origin into account.
PDEVOBJ pdoSrc( pSurfSrc->hdev() );
if( pSurfSrc->iType() == STYPE_DEVICE &&
pdoSrc.bValid() && pdoSrc.bMetaDriver())
{
bBadRects = ((erclSrc.left < pdoSrc.pptlOrigin()->x) ||
(erclSrc.top < pdoSrc.pptlOrigin()->y) ||
(erclSrc.right > pdoSrc.pptlOrigin()->x +
pSurfSrc->sizl().cx) ||
(erclSrc.bottom > pdoSrc.pptlOrigin()->y +
pSurfSrc->sizl().cy));
}
else
{
bBadRects = ((erclSrc.left < 0) ||
(erclSrc.top < 0) ||
(erclSrc.right > pSurfSrc->sizl().cx) ||
(erclSrc.bottom > pSurfSrc->sizl().cy));
}
if (bBadRects)
{
WARNING("NtGdiAlphaBlend -- source rectangle out of surface bounds");
}
//
// Make sure that source and destination rectangles don't overlap if the
// source surface is the same as the destination surface.
//
if (pSurfSrc == pSurfDst)
{
ERECTL erclIntersection = erclSrc;
erclIntersection *= erclDst;
if (!erclIntersection.bEmpty())
{
bBadRects = TRUE;
WARNING ("NtGdiAlphaBlend -- source and destination rectangles are on the same surface and overlap");
}
}
if (!bBadRects)
{
//
// check for quick out
//
if ((BlendFunction.SourceConstantAlpha == 255) &&
(!(BlendFunction.AlphaFormat & AC_SRC_ALPHA)) &&
(!(BlendFunction.BlendFlags & AC_USE_HIGHQUALITYFILTER)))
{
// BUGFIX #331222 2-2-2001
// Set stretch mode to COLORONCOLOR for duration of GreStretchBlt call
// AlphaBlend always point samples when stretching
BYTE jStretchBltMode = dcoDst.pdc->jStretchBltMode();
dcoDst.pdc->jStretchBltMode(COLORONCOLOR);
bStatus = GreStretchBlt(
hdcDst,
DstX,
DstY,
DstCx,
DstCy,
hdcSrc,
SrcX,
SrcY,
SrcCx,
SrcCy,
SRCCOPY,
0xffffffff);
bQuickStretch = TRUE;
dcoDst.pdc->jStretchBltMode(jStretchBltMode);
}
//
// no quick out
//
if (bStatus & !bQuickStretch)
{
XEPALOBJ palSrc(pSurfSrc->ppal());
XEPALOBJ palSrcDC(dcoSrc.ppal());
EXLATEOBJ xlo,xlo1,xloSrcDCto32,xloDstDCto32,xlo32toDstDC;
XLATEOBJ *pxlo,*pxloSrcDCto32,*pxloDstDCto32,*pxlo32toDstDC;
XEPALOBJ palRGB(gppalRGB);
//
// Get a translate object from source dc to BGRA
//
COLORREF crBackColor = dcoSrc.pdc->ulBackClr();
//
// src to dst
//
bStatus = xlo.bInitXlateObj(
NULL,
DC_ICM_OFF,
palSrc,
palDst,
palSrcDC,
palDstDC,
dcoSrc.pdc->crTextClr(),
dcoSrc.pdc->crBackClr(),
crBackColor
);
pxlo = xlo.pxlo();
//
// src to 32
//
bStatus &= xloSrcDCto32.bInitXlateObj(
NULL,
DC_ICM_OFF,
palSrc,
palRGB,
palSrcDC,
palSrcDC,
dcoSrc.pdc->crTextClr(),
dcoSrc.pdc->crBackClr(),
crBackColor
);
pxloSrcDCto32 = xloSrcDCto32.pxlo();
//
// translate from dst dc to BGRA
//
bStatus &= xloDstDCto32.bInitXlateObj(
NULL,
DC_ICM_OFF,
palDst,
palRGB,
palDstDC,
palDstDC,
dcoSrc.pdc->crTextClr(),
dcoSrc.pdc->crBackClr(),
crBackColor
);
pxloDstDCto32 = xloDstDCto32.pxlo();
//
// create xlate from 32 to dst dc
//
bStatus &= xlo32toDstDC.bInitXlateObj(
NULL,
DC_ICM_OFF,
palRGB,
palDst,
palDstDC,
palDstDC,
dcoSrc.pdc->crTextClr(),
dcoSrc.pdc->crBackClr(),
crBackColor
);
pxlo32toDstDC = xlo32toDstDC.pxlo();
// Compute destination clipping
ECLIPOBJ eco(dcoDst.prgnEffRao(), erclDst);
// Check the destination which is reduced by clipping.
if (eco.erclExclude().bEmpty())
{
// NTBUG #456213 2-4-2000 bhouse Clean up use of multiple return
// perf and size: don't return here
return(TRUE);
}
// Compute the exclusion rectangle.
ERECTL erclExclude = eco.erclExclude();
// If we are going to the same source, prevent bad overlap situations
// Expand exclusion rectangle to cover source rectangle
if (dcoSrc.pSurface() == dcoDst.pSurface())
{
if (erclSrc.left < erclExclude.left)
erclExclude.left = erclSrc.left;
if (erclSrc.top < erclExclude.top)
erclExclude.top = erclSrc.top;
if (erclSrc.right > erclExclude.right)
erclExclude.right = erclSrc.right;
if (erclSrc.bottom > erclExclude.bottom)
erclExclude.bottom = erclSrc.bottom;
}
// We might have to exclude the source or the target, get ready to do either.
DEVEXCLUDEOBJ dxo;
// Lock the source and target LDEVs
PDEVOBJ pdoDst(pSurfDst->hdev());
// They can't both be display
if (dcoSrc.bDisplay())
{
ERECTL ercl(0,0,pSurfSrc->sizl().cx,pSurfSrc->sizl().cy);
if (dcoSrc.pSurface() == dcoDst.pSurface())
ercl *= erclExclude;
else
ercl *= erclSrc;
dxo.vExclude(dcoSrc.hdev(),&ercl,NULL);
}
else if (dcoDst.bDisplay())
{
dxo.vExclude(dcoDst.hdev(),&erclExclude,&eco);
}
//
// Note Win2k App compat: We do
// mirroring only when the caller has
// asked for it. This it to make sure
// apps that worked on win2k are not
// suddenly mirrored causing problems.
//
// If we need to do RTL layout then
// we create a temporary surface and
// mirror the source into it. Then
// we use it as the source surface.
//
SURFMEM surfMirrorSrc;
if (bMirrorBitmap &&
(MIRRORED_DC(dcoDst.pdc)) &&
(!MIRRORED_DC_NO_BITMAP_FLIP(dcoDst.pdc)))
{
// Create temporary surface
DEVBITMAPINFO dbmi;
dbmi.cxBitmap = pSurfSrc->sizl().cx;
dbmi.cyBitmap = pSurfSrc->sizl().cy;
dbmi.iFormat = pSurfSrc->iFormat();
dbmi.fl = 0;
XEPALOBJ palMirrorSrc(pSurfSrc->ppal());
dbmi.hpal = (HPALETTE)palMirrorSrc.hpal();
surfMirrorSrc.bCreateDIB(&dbmi,(VOID*)NULL);
if(!surfMirrorSrc.bValid())
{
WARNING("NtGdiAlphaBlend: Could not create surface to mirror the source");
return FALSE;
}
ERECTL erclMirrorSrc(0,0,pSurfSrc->sizl().cx,pSurfSrc->sizl().cy);
EPOINTL eptlMirrorSrcTopLeft(0,0);
// Copy source surface into the
// temporary
if(!(*PPFNGET(pdoSrc,CopyBits,pSurfSrc->flags()))(
surfMirrorSrc.ps->pSurfobj(),
pSurfSrc->pSurfobj(),
NULL,
NULL,
&erclMirrorSrc,
&eptlMirrorSrcTopLeft))
{
WARNING("NtGdiAlphaBlend: Could not mirror the source");
return FALSE;
}
// Mirror the temporary surface.
(*apfnMirror[surfMirrorSrc.ps->iFormat()])(surfMirrorSrc.ps);
// Use temporary surface as source.
pSurfSrc = surfMirrorSrc.ps;
}
//
// If the destination requires rotation, we allocate a surface and rotate the
// source surface into it.
//
SURFMEM surfMemTmpSrc;
//
// If the source is 32bits and has no per pixel Alpha, we need to first copy it
// and then add per pixel Alpha information (all this before the rotation).
//
SURFMEM surfMemTmpSrcPreRotate;
if (bRotationDst)
{
//
// allocate 32bpp BGRA surface for source, must be zero init
//
DEVBITMAPINFO dbmi;
dbmi.cxBitmap = erclDst.right - erclDst.left;
dbmi.cyBitmap = erclDst.bottom - erclDst.top;
dbmi.iFormat = BMF_32BPP;
dbmi.fl = 0;
XEPALOBJ palRGB(gppalRGB);
dbmi.hpal = (HPALETTE)palRGB.hpal();
bStatus &= surfMemTmpSrc.bCreateDIB(&dbmi, (VOID *) NULL);
//
// init DIB to transparent
// (so that portions of dst rect not covered by source rect are not drawn)
//
if (bStatus)
{
if (!(BlendFunction.AlphaFormat & AC_SRC_ALPHA))
{
//
// Source has no per pixel Alpha. Need to first
// copy source to a new bitmap, then set the per pixel Alpha,
// and only then rotate.
//
DEVBITMAPINFO dbmiPreRotate;
dbmiPreRotate.cxBitmap = erclSrc.right - erclSrc.left;
dbmiPreRotate.cyBitmap = erclSrc.bottom - erclSrc.top;
dbmiPreRotate.iFormat = BMF_32BPP;
dbmiPreRotate.fl = 0;
XEPALOBJ palRGB(gppalRGB);
dbmiPreRotate.hpal = (HPALETTE)palRGB.hpal();
bStatus = surfMemTmpSrcPreRotate.bCreateDIB(&dbmiPreRotate, (VOID *) NULL);
if (bStatus)
{
//
// Make sure the bitmap starts at (0,0), but remember the original
// starting point in eptlSrcTopLeft
//
EPOINTL eptlSrcTopLeft (erclSrc.left, erclSrc.top);
// Make sure the subtraction doesn't overflow
if (((erclSrc.left < 0) &&
(-erclSrc.left > MAXLONG/2) &&
(erclSrc.right > MAXLONG/2)) ||
((erclSrc.top < 0) &&
(-erclSrc.top > MAXLONG/2) &&
(erclSrc.top > MAXLONG/2)))
{
//
// Fail the call
//
WARNING("NtGdiAlphaBlend: source rectangle too large\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
erclSrc -= eptlSrcTopLeft;
//
// Only call EngCopyBits for non-empty rectangles
//
if ((erclSrc.right > erclSrc.left) &&
(erclSrc.bottom > erclSrc.top) &&
(eptlSrcTopLeft.x <= pSurfSrc->sizl().cx) &&
(eptlSrcTopLeft.y <= pSurfSrc->sizl().cy))
{
EngCopyBits(
surfMemTmpSrcPreRotate.ps->pSurfobj(),
pSurfSrc->pSurfobj(),
NULL,
pxloSrcDCto32,
&erclSrc,
&eptlSrcTopLeft
);
}
//
// Now set the Alpha channel to 255 (opaque)
//
PULONG pulDstY = (PULONG)surfMemTmpSrcPreRotate.ps->pvScan0();
PULONG pulDstLastY = (PULONG)((PBYTE)pulDstY +
(surfMemTmpSrcPreRotate.ps->lDelta() *
surfMemTmpSrcPreRotate.ps->sizl().cy));
LONG DstYCount = 0;
while (pulDstY != pulDstLastY)
{
if ((DstYCount >= erclSrc.top) &&
(DstYCount < erclSrc.bottom))
{
PULONG pulDstX = pulDstY;
PULONG pulDstLastX = pulDstX + surfMemTmpSrcPreRotate.ps->sizl().cx;
LONG DstXCount = 0;
while (pulDstX != pulDstLastX)
{
if ((DstXCount >= erclSrc.left) &&
(DstXCount < erclSrc.right))
{
*pulDstX = *pulDstX | 0xff000000;
}
DstXCount++;
pulDstX++;
}
}
DstYCount++;
pulDstY = (PULONG)((PBYTE)pulDstY + surfMemTmpSrcPreRotate.ps->lDelta());
}
//
// Set source surface to pre rotated bitmap, and set color
// translation to trivial
//
pSurfSrc = surfMemTmpSrcPreRotate.ps;
pxloSrcDCto32 = &xloIdent;
//
// Now we have an Alpha channel
//
BlendFunction.AlphaFormat |= AC_SRC_ALPHA;
}
}
//
// Source is 32bit with per pixel Alpha. Make sure everything
// is transparent before the EngPlgBlt call.
//
RtlFillMemoryUlong(surfMemTmpSrc.ps->pvBits(),
surfMemTmpSrc.ps->cjBits(),
0x00000000);
}
if (!bStatus)
{
//
// Fail the call
//
WARNING("NtGdiAlphaBlend: failed to create temporary DIB\n");
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
//
// Now define the parallelogram the source bitmap is mapped to in surfMemTmpSrc
//
EPOINTFIX eptlNewSrc[3];
eptlNewSrc[0] = EPOINTFIX(
pptfxDst[0].x - LTOFX(erclDstOrig.left),
pptfxDst[0].y - LTOFX(erclDstOrig.top)
);
eptlNewSrc[1] = EPOINTFIX(
pptfxDst[1].x - LTOFX(erclDstOrig.left),
pptfxDst[1].y - LTOFX(erclDstOrig.top)
);
eptlNewSrc[2] = EPOINTFIX(
pptfxDst[2].x - LTOFX(erclDstOrig.left),
pptfxDst[2].y - LTOFX(erclDstOrig.top)
);
EngPlgBlt(
surfMemTmpSrc.ps->pSurfobj(),
pSurfSrc->pSurfobj(),
NULL, // No mask
NULL, // No clipping object
pxloSrcDCto32,
NULL, // No color adjustment
NULL,
eptlNewSrc,
&erclSrc,
NULL,
COLORONCOLOR
);
//
// Now adjust the local variables. First fix color translation.
//
//
// src to dst
//
bStatus = xlo1.bInitXlateObj(
NULL,
DC_ICM_OFF,
palRGB,
palDst,
NULL,
palDstDC,
dcoSrc.pdc->crTextClr(),
dcoSrc.pdc->crBackClr(),
crBackColor
);
pxlo = xlo1.pxlo();
//
// src to 32
//
pxloSrcDCto32 = &xloIdent;
pSurfSrc = surfMemTmpSrc.ps;
erclSrc.left = 0;
erclSrc.top = 0;
erclSrc.right = erclDst.right - erclDst.left;
erclSrc.bottom = erclDst.bottom - erclDst.top;
}
if (bStatus)
{
//
// Inc the target surface uniqueness
//
INC_SURF_UNIQ(pSurfDst);
//
// Dispatch the call. Give it no mask.
//
//
// Check were on the same PDEV, we can't blt between
// different PDEV's. We could make blting between different
// PDEV's work easily. All we need to do force EngBitBlt to
// be called if the PDEV's aren't equal in the dispatch.
// EngBitBlt does the right thing.
//
if (dcoDst.hdev() == dcoSrc.hdev())
{
EBLENDOBJ eBlendObj;
eBlendObj.BlendFunction = BlendFunction;
eBlendObj.pxloSrcTo32 = pxloSrcDCto32;
eBlendObj.pxloDstTo32 = pxloDstDCto32;
eBlendObj.pxlo32ToDst = pxlo32toDstDC;
//
// dispatch to driver or engine
//
if ( (erclDst.right - erclDst.left) == (erclSrc.right - erclSrc.left) &&
(erclDst.bottom - erclDst.top) == (erclSrc.bottom - erclSrc.top) )
{
eBlendObj.BlendFunction.BlendFlags &= ~AC_USE_HIGHQUALITYFILTER;
}
bStatus = (*PPFNGET(pdoDst,AlphaBlend, pSurfDst->flags())) (
pSurfDst->pSurfobj(),
pSurfSrc->pSurfobj(),
&eco,
pxlo,
&erclDst,
&erclSrc,
(BLENDOBJ *)&eBlendObj
);
}
else
{
WARNING1("NtGdiAlphaBlend failed: source and destination surfaces not on same PDEV");
EngSetLastError(ERROR_INVALID_PARAMETER);
bStatus = FALSE;
}
}
else
{
WARNING1("bInitXlateObj failed in NtGdiAlphaBlend\n");
EngSetLastError(ERROR_INVALID_HANDLE);
bStatus = FALSE;
}
}
else
{
//
// Nothing to do--the call to GreStretchBlt succeeded.
//
NULL;
}
}
else
{
EngSetLastError(ERROR_INVALID_PARAMETER);
bStatus = FALSE;
}
}
else
{
WARNING1("NtGdiAlphaBlend failed - trying to read from unreadable surface\n");
EngSetLastError(ERROR_INVALID_HANDLE);
bStatus = FALSE;
}
}
else
{
bStatus = TRUE; // pSurfSrc is NULL
}
}
else
{
bStatus = TRUE; // pSurfDst is NULL
}
}
else
{
//
// Return True if we are in full screen mode.
//
bStatus = dcoDst.bFullScreen() | dcoSrc.bFullScreen();
}
}
else
{
bStatus = TRUE; // erclDst is empty
}
}
else
{
bStatus = FALSE;
EngSetLastError(ERROR_INVALID_PARAMETER);
WARNING1("Error in NtGdiAlphaBlend: source rotation is not allowed");
}
}
else
{
bStatus = FALSE;
EngSetLastError(ERROR_INVALID_PARAMETER);
WARNING1("NtGdiAlphaBlend failed: invalid src DC");
}
return(bStatus);
}