想做出像媒體播放器皮膚那樣造型奇特、背景透明、還能拖曳移動的視窗?關鍵在於:依據位圖透明色建立視窗區域 (HRGN),並處理 WM_NCHITTEST 讓客戶區響應標題列拖曳。本文提供完整 C++Builder 實作碼。
TImage),以左上角像素 (0,0) 顏色為透明基準色。CreatePolygonRgn 或逐行 CombineRgn(RGN_XOR) 合成最終區域。SetWindowRgn(Handle, hRgn, TRUE) 套用至視窗。BorderStyle = bsNone),在 Image.OnMouseDown 中發送 WM_NCLBUTTONDOWN, HTCAPTION 實現拖曳。void __fastcall TForm1::BmpToRgn() {
Image1->AutoSize = true;
Form1->AutoSize = true;
Form1->BorderStyle = bsNone;
TColor clrKey = Image1->Canvas->Pixels[0][0]; // 透明色
int w = Image1->Width, h = Image1->Height;
HRGN hRgn = CreateRectRgn(0, 0, w, h);
HRGN hTmp;
POINT* pts = new POINT[h * 2]; // 多邊形頂點緩衝
// 掃描線演算法:記錄每行首尾非透明點
for (int y = 0; y < h; y++) {
int xLeft = -1, xRight = -1;
for (int x = 0; x < w; x++)
if (Image1->Canvas->Pixels[x][y] != clrKey) { xLeft = x; break; }
for (int x = w-1; x >= 0; x--)
if (Image1->Canvas->Pixels[x][y] != clrKey) { xRight = x; break; }
if (xLeft != -1) {
// 排除透明像素:建立 1x1 矩形區域 XOR 移除
for (int x = xLeft; x <= xRight; x++)
if (Image1->Canvas->Pixels[x][y] == clrKey) {
hTmp = CreateRectRgn(x, y, x+1, y+1);
CombineRgn(hRgn, hRgn, hTmp, RGN_XOR);
DeleteObject(hTmp);
}
// 記錄多邊形頂點
pts[y*2].x = xLeft; pts[y*2].y = y;
pts[y*2+1].x = xRight; pts[y*2+1].y = y;
} else {
pts[y*2] = pts[y*2-2]; // 全透明行複製上一行
pts[y*2+1] = pts[y*2-1];
}
}
// 建立多邊形區域並與掃描線區域交集 (RGN_AND) 修正邊緣
hTmp = CreatePolygonRgn(pts, h*2, WINDING);
CombineRgn(hRgn, hRgn, hTmp, RGN_AND);
DeleteObject(hTmp);
delete[] pts;
SetWindowRgn(Handle, hRgn, TRUE); // 系統接管區域記憶體
}
// 拖曳移動
void __fastcall TForm1::Image1MouseDown(TObject*, TMouseButton Button, TShiftState, int, int) {
if (Button == mbLeft) {
ReleaseCapture();
Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
}
// 開啟新圖片重建區域
void __fastcall TForm1::Open1Click(TObject*) {
if (OpenPictureDialog1->Execute()) {
Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName);
BmpToRgn();
}
}
Pixels[x][y] 極慢。建議用 ScanLine 直接存取記憶體,或鎖定 TBitmap 後一次讀取。ExtCreateRegion 配合 RGNDATA 批次傳入矩形,避免大量 CombineRgn 呼叫。ExtCreateRegion 載入,啟動瞬間無延遲。GDI+ GraphicsPath 可直接從帶 Alpha 的影像建立路徑再轉區域,支援半透明邊緣(需 Vista+ DWM 配合)。純 GDI 只能做 1-bit 透明/不透明。