Windows XP 關機時螢幕會呈現平滑的灰階漸變淡出效果,而非簡單的灰色刷填。本文分析並實作此核心演算法:擷取全螢幕位圖 → 轉灰階 → 以 Alpha 混合逐漸疊加白色,達到動態漸變視覺效果。
HBITMAP CMyFade::FadeBitmap(HBITMAP hBmp, double dfTrans) // dfTrans: 0~100 透明度百分比
{
if (!hBmp) return NULL;
HDC hBufDC = CreateCompatibleDC(NULL);
HGDIOBJ hOldBuf = SelectObject(hBufDC, hBmp);
HDC hDirDC = CreateCompatibleDC(NULL);
BITMAP bm; GetObject(hBmp, sizeof(bm), &bm);
// 建立 32-bit DIB Section 以直接操作像素 (ARGB)
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bm.bmWidth;
bmi.bmiHeader.biHeight = bm.bmHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
UINT* pPixels = NULL;
HBITMAP hDirBmp = CreateDIBSection(hDirDC, &bmi, DIB_RGB_COLORS, (void**)&pPixels, NULL, 0);
if (!hDirBmp) { DeleteDC(hDirDC); SelectObject(hBufDC, hOldBuf); DeleteDC(hBufDC); return NULL; }
HGDIOBJ hOldDir = SelectObject(hDirDC, hDirBmp);
// 將原螢幕位圖複製至 DIB Section
BitBlt(hDirDC, 0, 0, bm.bmWidth, bm.bmHeight, hBufDC, 0, 0, SRCCOPY);
int nAlpha = (int)(255.0 * dfTrans / 100.0); // 0~255
int nSize = bm.bmWidth * bm.bmHeight;
for (int i = 0; i < nSize; i++)
{
// DIB Section 記憶體排列為 BGRA (小端序)
int iSrcB = pPixels[i] & 0xFF;
int iSrcG = (pPixels[i] >> 8) & 0xFF;
int iSrcR = (pPixels[i] >> 16) & 0xFF;
// 灰階轉換 (ITU-R BT.601 權重)
int iGrey = (iSrcR * 54 + iSrcG * 182 + iSrcB * 19) >> 8; // 近似 0.2126R + 0.7152G + 0.0722B
// Alpha 混合:灰階色 (R=G=B=iGrey) 與原色混合
// 目標 = Src * (1 - Alpha) + Grey * Alpha
int r = (iSrcR * (255 - nAlpha) + iGrey * nAlpha) >> 8;
int g = (iSrcG * (255 - nAlpha) + iGrey * nAlpha) >> 8;
int b = (iSrcB * (255 - nAlpha) + iGrey * nAlpha) >> 8;
pPixels[i] = (r << 16) | (g << 8) | b; // 寫回 BGR (無 Alpha 通道)
}
SelectObject(hDirDC, hOldDir);
DeleteDC(hDirDC);
SelectObject(hBufDC, hOldBuf);
DeleteDC(hBufDC);
return hDirBmp; // 呼叫端需負責 DeleteObject
}
pPixels),避免 GetPixel/SetPixel 極慢的效能。Y = 0.2126R + 0.7152G + 0.0722B,程式中以整數運算近似 (R*54 + G*182 + B*19) >> 8。Dst = Src*(1-a) + Grey*a,a = dfTrans/100。dfTrans 從 0 遞增至 100 即可產生動畫幀。>> 8,乘以權重後右移 8 位相當於除以 256,全整數運算無浮點開銷。WS_EX_TOPMOST | WS_EX_LAYERED)。BitBlt 擷取桌面至記憶體位圖。dfTrans = 0 → 100 (步進 5,Sleep 30ms):FadeBitmap(hDeskBmp, dfTrans) 取得漸變位圖。UpdateLayeredWindow 或 BitBlt 將結果繪製到全螢幕視窗。ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0) 真正關機。BitBlt 不包含游標,需額外 DrawIconEx 繪製游標。EnumDisplayMonitors 擷取各螢幕並拼接。ExitWindowsEx 需 SE_SHUTDOWN_NAME 特權,服務或管理員權限下執行較無問題。DwmEnableBlurBehindWindow、DirectComposition 或 Windows Animation Manager 實作更流暢的 GPU 加速特效。完整範例碼包含 CMyFade 類別與對話框測試專案,可從 VC 知識庫下載。歡迎交流:xuwenq88@126.com