|
|
/******************************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); }
|