2015-03-13 14 views
7

Per xtow, desidero disegnare una finestra di livello superiore con l'area non client standard e l'area client riempita con una bitmap che ha un canale alfa.Disegno di una finestra con una cornice standard e contenuto trasparente

Ora scopro il modo in cui ho implementato questo funziona su Windows 7, ma non esegue il rendering correttamente su Windows 8.1, lasciando dietro le immagini dei contenuti della finestra quando viene spostato o ingrandito.

Per studiare, ho fatto un semplice programma di test alpha-test, che

  • Usa DwmEnableBlurBehindWindow() per impostare una regione sfocatura che non si intersecano, in modo che i valori alfa nella finestra sono onorati, senza sfocature.
  • Utilizza BitBlt() per copiare una bitmap con alfa all'interno.
// 
// g++ alpha-test.cc -o alpha-test -mwindows -lgdiplus -ldwmapi 
// 

#define _WIN32_WINNT 0x0600 

#include <assert.h> 
#include <stdio.h> 
#include <windows.h> 
#include <gdiplus.h> 
#include <dwmapi.h> 

int width = 360; 
int height = 360; 
HBITMAP hBitmap; 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch (message) 
    { 
    case WM_PAINT: 
     { 
     PAINTSTRUCT ps; 
     HDC hdcUpdate = BeginPaint(hWnd, &ps); 

     RECT rc; 
     GetClientRect(hWnd, &rc); 
     HBRUSH hbrush = CreateSolidBrush(RGB(0,0,0)); 
     FillRect(hdcUpdate, &rc, hbrush); 
     DeleteObject(hbrush); 

     HDC hdcMem = CreateCompatibleDC(hdcUpdate); 
     HBITMAP hbmpold = (HBITMAP)SelectObject(hdcMem, hBitmap); 

     if (!BitBlt(hdcUpdate, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, hdcMem, 0, 0, SRCCOPY)) 
      { 
      printf("BitBlt failed: 0x%08x\n", (int)GetLastError()); 
      } 

     SelectObject(hdcMem, hbmpold); 
     DeleteDC(hdcMem); 

     EndPaint(hWnd, &ps); 
     } 
     return 0; 

    case WM_DESTROY: 
     PostQuitMessage(0); 
     return 0; 

    default: 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
} 

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) 
{ 
    ULONG_PTR gdiplusToken; 
    Gdiplus::GdiplusStartupInput gdiplusStartupInput; 
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 

    LPCTSTR szWindowClass = "TransparentClass"; 

    // Register class 
    WNDCLASSEX wcex = {0}; 
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style   = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC; 
    wcex.lpfnWndProc = WndProc; 
    wcex.cbClsExtra  = 0; 
    wcex.cbWndExtra  = 0; 
    wcex.hInstance  = hInstance; 
    wcex.hIcon   = NULL; 
    wcex.hCursor  = LoadCursor(NULL, IDC_ARROW); 
    wcex.lpszClassName = szWindowClass; 
    wcex.hIconSm  = NULL; 
    wcex.hbrBackground = (HBRUSH)CreateSolidBrush(0x00000000); 
    RegisterClassEx(&wcex); 

    // Create window 
    HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, 
          szWindowClass, 
          "Transparent Window", 
          WS_OVERLAPPED | WS_SYSMENU, 
          CW_USEDEFAULT, CW_USEDEFAULT, width, height, 
          NULL, NULL, hInstance, NULL); 

    Gdiplus::Bitmap *m_pImage = Gdiplus::Bitmap::FromFile(L"sample.png", FALSE); 
    Gdiplus::Color bg(0,0,0,0); 
    m_pImage->GetHBITMAP(bg, &hBitmap); 
    assert(hBitmap); 

    DWM_BLURBEHIND blurBehind = { 0 }; 
    blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; 
    blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1); 
    blurBehind.fEnable = TRUE; 
    blurBehind.fTransitionOnMaximized = FALSE; 
    DwmEnableBlurBehindWindow(hWnd, &blurBehind); 
    DeleteObject(blurBehind.hRgnBlur); 

    ShowWindow(hWnd, SW_SHOW); 

    // Main message loop 
    MSG msg; 
    while (GetMessage(&msg, NULL, 0, 0)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 

    return (int)msg.wParam; 
} 

Questa è davvero rotto? Come posso risolvere il mio codice? Si tratta di un bug/limitazione di Windows?

C'è un altro modo per raggiungere il mio obiettivo di disegnare una bitmap con alpha in una finestra con un bordo?

Aggiornamento: Ho fatto alcuni test utilizzando Direct2D e Direct3D per riempire l'area cliente con le bitmap, ma mis-reso allo stesso modo.

risposta

1

Il DWM non esegue più la sfocatura (questa funzione è stata considerata troppo potente e rimossa in Windows 8), quindi suppongo che non componga più correttamente l'area di sfondo della finestra, quindi non ottieni l'effetto alfa "automatico" che ti davano in Windows 7.

Questo è un modo insolito per disegnare finestre trasparenti per essere onesti. Usare UpdateLayeredWindow è il modo "ufficiale" e avrebbe il vantaggio di lavorare su Windows 8 e Windows 7.

+0

Forse mi manca qualcosa, ma penso che le finestre disegnate con UpdateLayeredWindow non abbiano un frame, che lo rende un po 'non-starter per i miei scopi, a meno che non ci sia un modo per aggirare questo ... – jturney

+0

@jturney Nota che in Windows 8 puoi usare lo stile 'WS_EX_LAYERED' su una finestra figlia, quindi potresti probabilmente fare questo con due percorsi di codice, utilizzando il metodo esistente per Windows 7 e versioni precedenti. Sei sicuro di non poterlo fare in Windows 7? (Non ho mai provato a abilitare nessuno degli stili di bordo nella finestra quando l'ho fatto in passato) –

Problemi correlati