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

打造透明、可移動、不規則圖形視窗:位圖區域法完整實作

     編輯:VC 教學

想做出像媒體播放器皮膚那樣造型奇特、背景透明、還能拖曳移動的視窗?關鍵在於:依據位圖透明色建立視窗區域 (HRGN),並處理 WM_NCHITTEST 讓客戶區響應標題列拖曳。本文提供完整 C++Builder 實作碼。

核心步驟

  1. 載入位圖(TImage),以左上角像素 (0,0) 顏色為透明基準色。
  2. 掃描每一掃描線,找出非透明像素的最左/最右邊界,建立矩形區域陣列。
  3. 用 CreatePolygonRgn 或逐行 CombineRgn(RGN_XOR) 合成最終區域。
  4. 呼叫 SetWindowRgn(Handle, hRgn, TRUE) 套用至視窗。
  5. 移除視窗框線 (BorderStyle = bsNone),在 Image.OnMouseDown 中發送 WM_NCLBUTTONDOWN, HTCAPTION 實現拖曳。

完整實作碼 (Form1.cpp)

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 載入,啟動瞬間無延遲。

進階:支援 Alpha 通道 (PNG)

GDI+ GraphicsPath 可直接從帶 Alpha 的影像建立路徑再轉區域,支援半透明邊緣(需 Vista+ DWM 配合)。純 GDI 只能做 1-bit 透明/不透明。

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