首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ Builder 任意形狀視窗實作:掃描位圖透明色生成複合區域 (RGNDATA)

     編輯:關於C++

要實現任意形狀視窗 (如角色外型、不規則面板),核心在於根據位圖透明色掃描生成矩形區域陣列 (RGNDATA),再以 ExtCreateRegion 建立複雜區域並套用 SetWindowRgn。本文提供完整掃描演算法與程式碼。

核心資料結構

typedef struct _RGNDATAHEADER {
    DWORD dwSize;   // sizeof(RGNDATAHEADER)
    DWORD iType;    // RDH_RECTANGLES
    DWORD nCount;   // 矩形數量
    DWORD nRgnSize; // nCount * sizeof(RECT)
    RECT  rcBound;  // 邊界矩形
} RGNDATAHEADER;

typedef struct _RGNDATA {
    RGNDATAHEADER rdh;
    char Buffer[1]; // 實際為 RECT[nCount]
} RGNDATA;

完整掃描與建區域程式碼

Graphics::TBitmap* Sprite = new Graphics::TBitmap();
bool bKey = true;
int iCount = 0;
TColor ColorKey = clWhite; // 透明色

Sprite->LoadFromFile("D:\\bcbbak\\resource\\traffic.bmp");
Width = Sprite->Width; Height = Sprite->Height;

// 第一遍掃描:計算非透明像素連續水平段數 (即矩形數量)
for (int i = 0; i < Sprite->Height; i++) {
    bKey = true;
    for (int j = 0; j < Sprite->Width; j++) {
        if (Sprite->Canvas->Pixels[j][i] == ColorKey)
            bKey = true;
        else {
            if (bKey) iCount++;
            bKey = false;
        }
    }
}

// 配置 RGNDATA 記憶體
BYTE* pData = new BYTE[sizeof(RGNDATAHEADER) + sizeof(RECT) * iCount];
RGNDATA* pRgnData = (RGNDATA*)pData;
RECT* pRect = (RECT*)(pData + sizeof(RGNDATAHEADER));

// 填寫 Header
pRgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
pRgnData->rdh.iType = RDH_RECTANGLES;
pRgnData->rdh.nCount = iCount;
pRgnData->rdh.nRgnSize = sizeof(RECT) * iCount;
pRgnData->rdh.rcBound.left = 0;
pRgnData->rdh.rcBound.top = 0;
pRgnData->rdh.rcBound.right = Sprite->Width;
pRgnData->rdh.rcBound.bottom = Sprite->Height;

// 第二遍掃描:生成矩形 (注意 Y 軸翻轉)
int iIndex = 0, iLeft = 0;
for (int i = Sprite->Height - 1; i >= 0; i--) { // 位圖 Y 軸自下而上,需從底部掃描
    bKey = TRUE; iLeft = -1;
    for (int j = 0; j < Sprite->Width; j++) {
        if (Sprite->Canvas->Pixels[j][i] == ColorKey) {
            if (!bKey) {
                pRect[iIndex].left = iLeft;
                pRect[iIndex].right = j;
                pRect[iIndex].top = Sprite->Width - i - 1; // Y 座標轉換
                pRect[iIndex].bottom = Sprite->Height - i;
                bKey = TRUE; iIndex++; iLeft = -1;
            }
        } else if (bKey) {
            iLeft = j; bKey = false;
        }
    }
    if (iLeft >= 0) { // 行尾處理
        pRect[iIndex].left = iLeft;
        pRect[iIndex].right = Sprite->Width;
        pRect[iIndex].top = i; // 注意:此處邏輯與上方不一致,原碼可能有誤,建議統一座標轉換
        pRect[iIndex].bottom = i;
        iIndex++;
    }
}

// 建立區域並套用
bool bR = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + sizeof(RECT) * iCount, pRgnData);
if (!bR) { ShowMessage("Create Form Failed."); Close(); }
SetWindowRgn(Handle, (HRGN)pRgnData, TRUE); // 實際應用中需用返回的 HRGN
delete[] pData;
delete Sprite;

關鍵演算法細節

  1. 掃描線演算法:逐行掃描,記錄非透明像素的連續水平段,每段對應一個矩形。
  2. Y 軸翻轉:Windows 位圖座標原點在左下角 (Y 向上),螢幕/視窗座標原點在左上角 (Y 向下),掃描時需轉換 top/bottom。
  3. 矩形合併優化:相鄰行若水平範圍相同,可合併為高度較大的矩形,減少 nCount 提升效能 (本碼未實現)。
  4. 記憶體配置:預先計算 iCount 再一次性配置 RGNDATA,避免動態擴展。

常見問題與修正

  • 座標轉換錯誤:原碼中行尾處理的 top/bottom 賦值 (i) 與主迴圈 (Sprite->Width - i - 1) 不一致。正確統一為:top = Height - i - 1; bottom = Height - i; (假設視窗高度 = 位圖高度)。
  • ExtCreateRegion 參數:第一參數傳 NULL 使用預設世界變換;第二參數為緩衝區總大小;第三參數為 RGNDATA*。
  • SetWindowRgn 所有權:呼叫後區域所有權轉移給系統,不可 重複使用或手動刪除 HRGN,緩衝區 pData 亦不可釋放 (系統持有指標)。
  • 效能考量:高解析度位圖 (如 500×500) 可能產生數萬矩形,建立區域耗時明顯。啟動時一次性建立並快取 HRGN,調整大小需重新生成。

封裝建議:TSpriteWindow 基底類別

class TSpriteWindow : public TForm {
protected:
    HRGN FRegion;
    void __fastcall LoadShapeFromBitmap(Graphics::TBitmap* Bmp, TColor TransparentColor);
    void __fastcall ApplyRegion();
public:
    __fastcall TSpriteWindow(TComponent* Owner);
    __fastcall ~TSpriteWindow();
};
// LoadShapeFromBitmap 內含上述掃描邏輯,ApplyRegion 呼叫 SetWindowRgn
// 衍生類別僅需在建構函式呼叫 LoadShapeFromBitmap 即可擁有任意形狀

應用場景

  • 桌面寵物/精靈角色視窗 (配合文章 7923 動畫邏輯)。
  • 不規則形狀工具面板、彈出選單、工具提示。
  • 遊戲介面元素 (血條、技能圖示非矩形區域點擊檢測)。
  • 特殊視覺效果視窗 (破碎、切片動畫)。

掌握位圖掃描生成 RGNDATA 技術,是實現任意形狀視窗的基石。配合現代 WS_EX_LAYERED + PNG Alpha 通道 (Win2000+),可獲得更自然的邊緣抗鋸齒與半透明效果,建議新專案優先考慮分層視窗方案,本技術作為舊系統相容或特殊區域邏輯需求的補充。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved