|
|
/****************************** Module Header ******************************\
* Module Name: shadow.c * * Copyright (c) 1985 - 2000, Microsoft Corporation * * Drop shadow support. * * History: * 04/12/2000 vadimg created * 02/12/2001 msadek added rounded rectangular shadow support * for rectangular windows * 05/08/2001 msadek rewrote the non rounded corners shadow algorithm * to work well with regional windows, correct visuall effect. \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
// shadow horizontal and veritcal offsets
#define CX_SHADOW 5
#define CY_SHADOW 5
#define C_SHADOW CX_SHADOW
// black as the shadow color
#define RGBA_SHADOW 0x00FFFFFF
// white as the transparent color
#define RGBA_TRANSPARENT 0x00000000
typedef struct tagSHADOW *PSHADOW; typedef struct tagSHADOW { PWND pwnd; // window we're shadowing
PWND pwndShadow; // the shadow window we create
PSHADOW pshadowNext; // link to the next shadow struct
} SHADOW;
PSHADOW gpshadowFirst;
// Macro used to map gray scale shadow values to alpha blending scale.
#define ALPHA(x) ((255 - (x)) << 24)
#define ARGB(a, r, g, b) (((DWORD)a<<24)|((DWORD)r<<16)|((DWORD)g<<8)|b)
// Gray scale values for the shadow grades
#define GS01 255
#define GS02 254
#define GS03 253
#define GS04 252
#define GS05 250
#define GS06 246
#define GS07 245
#define GS08 242
#define GS09 241
#define GS10 227
#define GS11 217
#define GS12 213
#define GS13 212
#define GS14 199
#define GS15 180
#define GS16 172
#define GS17 171
#define GS18 155
#define GS19 144
#define GS20 142
// pre-computed alpha values for the shadow
CONST BYTE grgShadow[C_SHADOW] = { GS04, GS09, GS13, GS17, GS20, };
CONST ULONG TopRightLTR [CY_SHADOW][CX_SHADOW] = { ALPHA(GS08), ALPHA(GS06), ALPHA(GS05), ALPHA(GS03), ALPHA(GS02), ALPHA(GS11), ALPHA(GS10), ALPHA(GS09), ALPHA(GS05), ALPHA(GS02), ALPHA(GS15), ALPHA(GS14), ALPHA(GS10), ALPHA(GS07), ALPHA(GS03), ALPHA(GS18), ALPHA(GS15), ALPHA(GS11), ALPHA(GS08), ALPHA(GS03), ALPHA(GS19), ALPHA(GS16), ALPHA(GS12), ALPHA(GS09), ALPHA(GS04), };
CONST ULONG RightLTR [CX_SHADOW] = { ALPHA(GS20), ALPHA(GS17), ALPHA(GS13), ALPHA(GS09), ALPHA(GS04), };
CONST ULONG BottomRightLTR [CY_SHADOW][CX_SHADOW] = { ALPHA(GS18), ALPHA(GS15), ALPHA(GS11), ALPHA(GS08), ALPHA(GS03), ALPHA(GS15), ALPHA(GS14), ALPHA(GS10), ALPHA(GS07), ALPHA(GS03), ALPHA(GS11), ALPHA(GS10), ALPHA(GS09), ALPHA(GS05), ALPHA(GS02), ALPHA(GS08), ALPHA(GS06), ALPHA(GS05), ALPHA(GS03), ALPHA(GS02), ALPHA(GS03), ALPHA(GS03), ALPHA(GS02), ALPHA(GS02), ALPHA(GS01), };
CONST ULONG Bottom [CY_SHADOW] = { ALPHA(GS20), ALPHA(GS17), ALPHA(GS13), ALPHA(GS09), ALPHA(GS04), };
CONST ULONG BottomLeftLTR [CY_SHADOW][CX_SHADOW] = { ALPHA(GS08), ALPHA(GS11), ALPHA(GS15), ALPHA(GS18), ALPHA(GS19), ALPHA(GS06), ALPHA(GS10), ALPHA(GS14), ALPHA(GS15), ALPHA(GS16), ALPHA(GS05), ALPHA(GS09), ALPHA(GS10), ALPHA(GS11), ALPHA(GS12), ALPHA(GS03), ALPHA(GS05), ALPHA(GS07), ALPHA(GS08), ALPHA(GS09), ALPHA(GS02), ALPHA(GS02), ALPHA(GS03), ALPHA(GS03), ALPHA(GS04), }; /***************************************************************************\
* DrawWindowShadow * \***************************************************************************/
BOOL DrawWindowShadow(PWND pwnd, HDC hdc, BOOL fRTL, BOOL fForceComplexRgn, PBOOL pfSimpleRgn) { HRGN hrgn1, hrgn2; RECT rc; HBRUSH hBrushShadow; BOOL bRet = FALSE;
UserAssert(pfSimpleRgn != NULL); hrgn1 = GreCreateRectRgn(0, 0, 0, 0); hrgn2 = GreCreateRectRgn(0, 0, 0, 0);
if (hrgn1 == NULL || hrgn2 == NULL) { goto Cleanup; }
/*
* Handle the case when the window is a rectangle or a regional window. */ if (pwnd->hrgnClip == NULL || TestWF(pwnd, WFMAXFAKEREGIONAL)) { rc = pwnd->rcWindow; OffsetRect(&rc, -rc.left, -rc.top); GreSetRectRgn(hrgn1, 0, 0, rc.right, rc.bottom); *pfSimpleRgn = TRUE; } else { GreCombineRgn(hrgn1, pwnd->hrgnClip, NULL, RGN_COPY); GreOffsetRgn(hrgn1, -pwnd->rcWindow.left, -pwnd->rcWindow.top); *pfSimpleRgn = FALSE; }
/*
* Offset the window by the shadow offsets and fill the difference * with the shadow color. The result will be window's shadow. */ GreCombineRgn(hrgn2, hrgn1, NULL, RGN_COPY); if (fRTL) { GreOffsetRgn(hrgn1, CX_SHADOW, 0); GreOffsetRgn(hrgn2, 0, CY_SHADOW); } else { GreOffsetRgn(hrgn2, CX_SHADOW, CY_SHADOW); } bRet = TRUE; if (!*pfSimpleRgn || fForceComplexRgn) { int i; BYTE gs;
for (i = C_SHADOW ; i > 0; i--) { gs = grgShadow[i - 1]; hBrushShadow = GreCreateSolidBrush(RGB(gs , gs , gs)); if (hBrushShadow == NULL) { bRet = FALSE; goto Cleanup; } NtGdiFrameRgn(hdc, hrgn2, hBrushShadow, i, i); GreDeleteObject(hBrushShadow); } GreFillRgn(hdc, hrgn1, (HBRUSH)GreGetStockObject(BLACK_BRUSH)); } else { GreCombineRgn(hrgn2, hrgn2, hrgn1, RGN_DIFF); GreFillRgn(hdc, hrgn2, (HBRUSH)GreGetStockObject(WHITE_BRUSH)); }
Cleanup: GreDeleteObject(hrgn1); GreDeleteObject(hrgn2); return bRet; }
/***************************************************************************\
* DrawTopLogicallyRightCorner * * Draw the shadow effect of the top, logically right (visually right for LTR, left for RTL window layout) * corner of a rounded rectangular shadow. * * History: * 02/12/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawTopLogicallyRightCorner(VOID* pBits, LONG cx, LONG cy, BOOL fRTL) { LONG i, j; ULONG* ppixel;
if (fRTL) { for (i = CY_SHADOW; i < (2 * CY_SHADOW); i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + ((cy - i - 1) * cx) + j; *ppixel = TopRightLTR[i - CY_SHADOW][CX_SHADOW - 1 - j]; } } } else { for (i = CY_SHADOW; i < (2 * CY_SHADOW); i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + ((cy - i) * cx) - CX_SHADOW + j; *ppixel = TopRightLTR[i - CY_SHADOW][j]; } } } }
/***************************************************************************\
* DrawLogicallyRightSide * * Draw the shadow effect of the logically right (visually right for LTR, left for RTL window layout) * side of a rounded rectangular shadow. * * History: * 02/12/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawLogicallyRightSide(VOID* pBits, LONG cx, LONG cy, BOOL fRTL) { LONG i, j; ULONG* ppixel;
if (fRTL) { for (i = (2 * CY_SHADOW); i < (cy - CY_SHADOW); i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + (cx * ( cy - i -1)) + j; *ppixel = RightLTR[CX_SHADOW - 1 - j]; } } } else { for (i = (2 * CY_SHADOW); i < (cy - CY_SHADOW); i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + (cx * ( cy - i -1)) + (cx - CX_SHADOW) + j; *ppixel = RightLTR[j]; } } } }
/***************************************************************************\
* DrawBottomLogicallyRightCorner * * Draw the shadow effect of the bottom logically right (visually right for LTR, left for RTL window layout) * side of a rounded rectangular shadow. * * History: * 02/12/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawBottomLogicallyRightCorner(VOID* pBits, LONG cx, BOOL fRTL) { LONG i, j; ULONG* ppixel;
if (fRTL) { for (i = 0; i < CY_SHADOW; i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + ((CY_SHADOW - i - 1) * cx) + j; *ppixel = BottomRightLTR[i][CX_SHADOW - 1 - j]; } } } else { for (i = 0; i < CY_SHADOW; i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + ((CY_SHADOW - i) * cx) - CX_SHADOW + j; *ppixel = BottomRightLTR[i][j]; } } } }
/***************************************************************************\
* DrawBottomSide * * Draw the shadow effect of the bottom side of a rounded rectangular shadow. * * History: * 02/12/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawBottomSide(VOID* pBits, LONG cx, BOOL fRTL) { LONG i, j; ULONG* ppixel;
if (fRTL) { for (i = 0; i < CY_SHADOW; i++) { for (j = CX_SHADOW; j < (cx - (2 * CX_SHADOW)); j++) { ppixel = (ULONG*)pBits + ((CY_SHADOW - i - 1) * cx) + j; *ppixel = Bottom[i]; } } } else { for (i = 0; i < CY_SHADOW; i++) { for (j = (2 * CX_SHADOW); j < (cx - CX_SHADOW); j++) { ppixel = (ULONG*)pBits + ((CY_SHADOW - i - 1) * cx) + j; *ppixel = Bottom[i]; } } } }
/***************************************************************************\
* DrawBottomLogicallyLeftCorner * * Draw the shadow effect of the bottom logically left (visually left for LTR, right for RTL window layout) * side of a rounded rectangular shadow. * * History: * 02/12/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawBottomLogicallyLeftCorner(VOID* pBits, LONG cx, BOOL fRTL) { LONG i, j; ULONG* ppixel;
if (fRTL) { for (i = 0; i < CY_SHADOW; i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + ((CY_SHADOW - i) * cx) - (2 * CX_SHADOW) + j; *ppixel = BottomLeftLTR[i][CX_SHADOW - 1 -j]; } } } else { for (i = 0; i < CY_SHADOW; i++) { for (j = 0; j < CX_SHADOW; j++) { ppixel = (ULONG*)pBits + ((CY_SHADOW - i - 1) * cx) + CX_SHADOW + j; *ppixel = BottomLeftLTR[i][j]; } } } }
/***************************************************************************\
* DrawRoundedRectangularShadow * Draw a rounded rectangular shadow effect. * Does not search for shadow pixel location in the bitmap but rather assumes * it to be the corners of the bitmap. * * History: * 02/12/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawRoundedRectangularShadow(VOID* pBits, LONG cx, LONG cy, BOOL fRTL) { DrawTopLogicallyRightCorner(pBits, cx, cy, fRTL); DrawLogicallyRightSide(pBits, cx, cy, fRTL); DrawBottomLogicallyRightCorner(pBits, cx, fRTL); DrawBottomSide(pBits, cx, fRTL); DrawBottomLogicallyLeftCorner(pBits, cx, fRTL); }
/***************************************************************************\
* DrawRegionalShadow * Search for shadow pixel location in the bitmap (those with gray scale in grgShadow * and adjust the alpha values. * * * History: * 05/08/2001 Mohamed Sadek [msadek] created \***************************************************************************/
_inline void DrawRegionalShadow(VOID* pBits, LONG cx, LONG cy) { LONG i, j, k; ULONG* pixel; BYTE gs;
for (i = 0; i < cy; i++) { for (j = 0; j < cx; j++) { pixel = (ULONG*)pBits + (cy - 1 - i) * cx + j; for (k = 0; k < C_SHADOW; k++) { gs = grgShadow[k]; if (*pixel == ARGB(0, gs, gs, gs)) { *pixel = ALPHA(gs); } } } } }
/***************************************************************************\
* GenerateWindowShadow * \***************************************************************************/
HBITMAP GenerateWindowShadow(PWND pwnd, HDC hdc) { BITMAPINFO bmi; HBITMAP hbm; VOID* pBits; LONG cx, cy; RECT rc; BOOL fRTL = TestWF(pwnd, WEFLAYOUTRTL); BOOL fSimpleRgn, fForceComplexRgn = FALSE;
rc = pwnd->rcWindow; OffsetRect(&rc, -rc.left, -rc.top); /*
* Doesn't make sense to have a shadow for a window with zero height or width */ if (IsRectEmpty(&rc)) { return NULL; }
rc.right += CX_SHADOW; rc.bottom += CY_SHADOW;
cx = rc.right; cy = rc.bottom;
/*
* Create the DIB section. */ RtlZeroMemory(&bmi, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = cx; bmi.bmiHeader.biHeight = cy; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB;
hbm = GreCreateDIBitmapReal(hdc, 0, NULL, &bmi, DIB_RGB_COLORS, sizeof(bmi), 0, NULL, 0, NULL, 0, 0, &pBits);
if (hbm == NULL) { return NULL; }
/*
* Fill the dib section with the transparent color and then * draw the shadow on top of it. */ GreSelectBitmap(hdc, hbm); FillRect(hdc, &rc, (HBRUSH)GreGetStockObject(BLACK_BRUSH));
/*
* Rectangular window shadow assumes the bitmap dimension to be greater than * or equal 2 * CX_SHADOW and 2 * CY_SHADOW else it will overrun bitmap buffer. * and in order to get a real rectangular shadow, the dimension should be 3 * CX_SHADOW * amd 3 * CY_SHADOW. */ if ( (cx < 3 * CX_SHADOW) || (cy < 3 * CY_SHADOW)) { fForceComplexRgn = TRUE; } if (!DrawWindowShadow(pwnd, hdc, fRTL, fForceComplexRgn, &fSimpleRgn)) { return NULL; } if (fSimpleRgn && !fForceComplexRgn) { DrawRoundedRectangularShadow(pBits, cx, cy, fRTL); return hbm; } DrawRegionalShadow(pBits, cx, cy); return hbm; }
/***************************************************************************\
* FindShadow * \***************************************************************************/
PSHADOW FindShadow(PWND pwnd) { PSHADOW pshadow;
for (pshadow = gpshadowFirst; pshadow != NULL; pshadow = pshadow->pshadowNext) { if (pshadow->pwnd == pwnd) { return pshadow; } } return NULL; }
/***************************************************************************\
* WindowHasShadow * \***************************************************************************/
BOOL WindowHasShadow(PWND pwnd) { BOOL fHasShadow = FALSE;
if (TestWF(pwnd, WFVISIBLE)) { PSHADOW pshadow = FindShadow(pwnd); fHasShadow = (pshadow != NULL); } else { /*
* The window isn't currently visible, so there is no shadow window. * We need to return if the window *would* have a shadow if it were * shown. */ if (TestCF(pwnd, CFDROPSHADOW)) { fHasShadow = TRUE;
if ((GETFNID(pwnd) == FNID_MENU) && (!TestALPHA(MENUFADE)) && TestEffectUP(MENUANIMATION)) { fHasShadow = FALSE; } }
if (!TestALPHA(DROPSHADOW)) { fHasShadow = FALSE; } }
return fHasShadow; }
/***************************************************************************\
* ApplyShadow * \***************************************************************************/
BOOL ApplyShadow(PWND pwnd, PWND pwndShadow) { POINT pt, ptSrc = {0, 0}; SIZE size; BLENDFUNCTION blend; HDC hdcShadow; HBITMAP hbmShadow; BOOL fRet;
hdcShadow = GreCreateCompatibleDC(gpDispInfo->hdcScreen);
if (hdcShadow == NULL) { return FALSE; }
hbmShadow = GenerateWindowShadow(pwnd, hdcShadow); if (hbmShadow == NULL) { GreDeleteDC(hdcShadow); return FALSE; }
pt.x = pwnd->rcWindow.left; pt.y = pwnd->rcWindow.top; size.cx = pwnd->rcWindow.right - pwnd->rcWindow.left + CX_SHADOW; size.cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top + CY_SHADOW;
if (TestWF(pwnd, WEFLAYOUTRTL)) { pt.x -= CX_SHADOW; }
blend.BlendOp = AC_SRC_OVER; blend.BlendFlags = 0; blend.AlphaFormat = AC_SRC_ALPHA; blend.SourceConstantAlpha = 255;
fRet = _UpdateLayeredWindow(pwndShadow, NULL, &pt, &size, hdcShadow, &ptSrc, 0, &blend, ULW_ALPHA);
GreDeleteDC(hdcShadow); GreDeleteObject(hbmShadow);
return fRet; }
/***************************************************************************\
* MoveShadow * \***************************************************************************/
VOID MoveShadow(PWND pwnd) { PSHADOW pshadow = FindShadow(pwnd); POINT pt;
if (pshadow == NULL) { return; }
pt.x = pwnd->rcWindow.left; pt.y = pwnd->rcWindow.top;
_UpdateLayeredWindow(pshadow->pwndShadow, NULL, &pt, NULL, NULL, NULL, 0, NULL, 0); }
/***************************************************************************\
* UpdateShadowShape * \***************************************************************************/
VOID UpdateShadowShape(PWND pwnd) { PSHADOW pshadow = FindShadow(pwnd);
if (pshadow == NULL) { return; }
ApplyShadow(pshadow->pwnd, pshadow->pwndShadow); }
/***************************************************************************\
* xxxUpdateShadowZorder * \***************************************************************************/
VOID xxxUpdateShadowZorder(PWND pwnd) { TL tlpwnd; PWND pwndShadow; PSHADOW pshadow = FindShadow(pwnd);
if (pshadow == NULL) { return; }
pwndShadow = pshadow->pwndShadow;
if (TestWF(pwnd, WEFTOPMOST) && !TestWF(pwndShadow, WEFTOPMOST)) { SetWF(pwndShadow, WEFTOPMOST); } else if (!TestWF(pwnd, WEFTOPMOST) && TestWF(pwndShadow, WEFTOPMOST)) { ClrWF(pwndShadow, WEFTOPMOST); }
ThreadLock(pwndShadow, &tlpwnd);
xxxSetWindowPos(pwndShadow, pwnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
ThreadUnlock(&tlpwnd);
} /***************************************************************************\
* xxxRemoveShadow * * Given the shadowed window, destroy the shadow window, cleanup the * memory used by the shadow structure and remove it from the list. \***************************************************************************/
VOID xxxRemoveShadow(PWND pwnd) { PSHADOW* ppshadow; PSHADOW pshadow; PWND pwndT;
CheckLock(pwnd);
ppshadow = &gpshadowFirst;
while (*ppshadow != NULL) {
pshadow = *ppshadow;
if (pshadow->pwnd == pwnd) {
pwndT = pshadow->pwndShadow;
*ppshadow = pshadow->pshadowNext; UserFreePool(pshadow);
xxxDestroyWindow(pwndT);
break; }
ppshadow = &pshadow->pshadowNext; } }
/***************************************************************************\
* RemoveShadow * * Given a shadow structure pointer, search for it in the list and remove it \***************************************************************************/
VOID RemoveShadow(PSHADOW pshadow) { PSHADOW* ppshadow; PSHADOW pshadowT; ppshadow = &gpshadowFirst;
while (*ppshadow != NULL) {
pshadowT = *ppshadow;
if (pshadowT == pshadow) { *ppshadow = pshadowT->pshadowNext; UserFreePool(pshadowT); break; }
ppshadow = &pshadowT->pshadowNext; } }
/***************************************************************************\
* CleanupShadow * * Given the shadow window, remove the shadow structure from the list and * cleanup the memory used by the shadow structure. \***************************************************************************/
VOID CleanupShadow(PWND pwndShadow) { PSHADOW* ppshadow; PSHADOW pshadow;
CheckLock(pwndShadow);
ppshadow = &gpshadowFirst;
while (*ppshadow != NULL) {
pshadow = *ppshadow;
if (pshadow->pwndShadow == pwndShadow) { *ppshadow = pshadow->pshadowNext; UserFreePool(pshadow); break; }
ppshadow = &pshadow->pshadowNext; } } /***************************************************************************\
* xxxAddShadow * \***************************************************************************/
BOOL xxxAddShadow(PWND pwnd) { PWND pwndShadow; DWORD ExStyle; TL tlpwnd; TL tlpool; PSHADOW pshadow;
CheckLock(pwnd);
if (!TestALPHA(DROPSHADOW)) { return FALSE; }
if (FindShadow(pwnd)) { return TRUE; }
if ((pshadow = (PSHADOW)UserAllocPool(sizeof(SHADOW), TAG_SHADOW)) == NULL) { return FALSE; }
ThreadLockPool(PtiCurrent(), pshadow, &tlpool);
ExStyle = WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TRANSPARENT; if (TestWF(pwnd, WEFTOPMOST)) { ExStyle |= WS_EX_TOPMOST; }
pwndShadow = xxxNVCreateWindowEx(ExStyle, (PLARGE_STRING)gatomShadow, NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, hModuleWin, NULL, WINVER);
if (pwndShadow == NULL || !ApplyShadow(pwnd, pwndShadow)) { UserFreePool(pshadow); ThreadUnlockPool(PtiCurrent(), &tlpool);
if (pwndShadow != NULL) { ThreadLock(pwndShadow, &tlpwnd); xxxDestroyWindow(pwndShadow); ThreadUnlock(&tlpwnd); }
return FALSE; }
pshadow->pshadowNext = gpshadowFirst; gpshadowFirst = pshadow;
pshadow->pwnd = pwnd; pshadow->pwndShadow = pwndShadow;
/*
* Since we added it the global list, we need to change the way * we lock its pool. */ ThreadUnlockPool(PtiCurrent(), &tlpool); ThreadLockPoolCleanup(PtiCurrent(), pshadow, &tlpool, RemoveShadow); ThreadLock(pwndShadow, &tlpwnd);
xxxSetWindowPos(pwndShadow, pwnd, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
ThreadUnlock(&tlpwnd);
ThreadUnlockPool(PtiCurrent(), &tlpool);
return TRUE; }
/***************************************************************************\
* FAnyShadows * \***************************************************************************/
BOOL FAnyShadows(VOID) { return (gpshadowFirst != NULL); }
|