2010-10-19 29 views
39

Estoy creando lo que debería ser una aplicación Win32 C++ muy simple cuyo único propósito es mostrar SOLO un PNG semitransparente. La ventana no debe tener ningún cromo, y toda la opacidad debe controlarse en el PNG.Creando una ventana transparente en C++ Win32

Mi problema es que la ventana no se vuelve a pintar cuando cambia el contenido debajo de la ventana, por lo que las áreas transparentes de PNG se "atascan" con lo que estaba debajo de la ventana cuando se inició la aplicación.

Aquí está la línea en la que tengo que instalar la nueva ventana:

hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0); 

Para la llamada a RegisterClassEx, tengo este conjunto para el fondo:

wcex.hbrBackground = (HBRUSH)0; 

Aquí está mi manejador de mensajes WM_PAINT:

case WM_PAINT: 
{ 
    hdc = BeginPaint(hWnd, &ps); 
    Gdiplus::Graphics graphics(hdc); 
    graphics.DrawImage(*m_pBitmap, 0, 0); 
    EndPaint(hWnd, &ps); 
    break; 
} 

Una cosa a tener en cuenta es que la aplicación siempre está acoplada a la izquierda de t él pantalla y no se mueve. Pero, lo que está debajo de la aplicación puede cambiar a medida que el usuario abre, cierra o mueve ventanas debajo de ella.

Cuando se inicia la aplicación por primera vez, se ve perfecto. Las partes transparentes (y simi-transparentes) del PNG se muestran perfectamente. PERO, cuando el fondo debajo de la aplicación cambia, el fondo NO se actualiza, simplemente permanece igual desde que se inició la aplicación. De hecho, WM_PAINT (o WM_ERASEBKGND no se llama cuando cambia el fondo).

He estado jugando con esto por bastante tiempo y he estado cerca de estar 100% en lo cierto, pero no del todo. Por ejemplo, he intentado configurar el fondo para (HBRUSH) NULL_BRUSH y he intentado manejar WM_ERASEBKGND.

¿Qué se puede hacer para que la ventana se vuelva a pintar cuando cambie el contenido?

+0

SetBkMode y SetBkColor son las API que he utilizado para hacer el control de los padres transparente. –

Respuesta

32

yo era capaz de hacer exactamente lo que quería mediante el uso del código de la Parte 1 y la Parte 2 de esta serie: http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

Aquellos las publicaciones de blog hablan de mostrar una pantalla de presentación en Win32 C++, pero era casi idéntica a lo que tenía que hacer. Creo que la parte que me faltaba era que, en lugar de simplemente pintar PNG en la ventana usando GDI +, necesitaba usar la función UpdateLayeredWindow con el parámetro BLENDFUNCTION adecuado.Voy a pegar el método SetSplashImage a continuación, que se puede encontrar en la parte 2 en el siguiente enlace:

void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash) 
{ 
    // get the size of the bitmap 
    BITMAP bm; 
    GetObject(hbmpSplash, sizeof(bm), &bm); 
    SIZE sizeSplash = { bm.bmWidth, bm.bmHeight }; 

    // get the primary monitor's info 
    POINT ptZero = { 0 }; 
    HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); 
    MONITORINFO monitorinfo = { 0 }; 
    monitorinfo.cbSize = sizeof(monitorinfo); 
    GetMonitorInfo(hmonPrimary, &monitorinfo); 

    // center the splash screen in the middle of the primary work area 
    const RECT & rcWork = monitorinfo.rcWork; 
    POINT ptOrigin; 
    ptOrigin.x = 0; 
    ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy)/2; 

    // create a memory DC holding the splash bitmap 
    HDC hdcScreen = GetDC(NULL); 
    HDC hdcMem = CreateCompatibleDC(hdcScreen); 
    HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash); 

    // use the source image's alpha channel for blending 
    BLENDFUNCTION blend = { 0 }; 
    blend.BlendOp = AC_SRC_OVER; 
    blend.SourceConstantAlpha = 255; 
    blend.AlphaFormat = AC_SRC_ALPHA; 

    // paint the window (in the right location) with the alpha-blended bitmap 
    UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash, 
     hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA); 

    // delete temporary objects 
    SelectObject(hdcMem, hbmpOld); 
    DeleteDC(hdcMem); 
    ReleaseDC(NULL, hdcScreen); 
} 
+0

es la mejor respuesta, también es compatible con la combinación alfa PNG automática cuando se utiliza con WIC (Windows Imaging Component) a través de la interfaz COM. – dns

+0

Terminó perdiendo mucho tiempo con AlphaBlend. Esta solución es, probablemente, una de las formas más fáciles de lograr Mezcla alfa por píxel, especialmente cuando se está actualizando la ventana. – TheBlueNotebook

16

Use SetLayeredWindowAttributes, esto le permite establecer un color de máscara que se volverá transparente, permitiendo el paso del fondo.

http://msdn.microsoft.com/en-us/library/ms633540(VS.85).aspx

También tendrá que configurar su ventana con la bandera en capas, por ejemplo,

SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); 

Después de eso es bastante simple:

// Make red pixels transparent: 
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY); 

Cuando su PNG contiene píxeles semitransparentes que desea combinar con el fondo, esto se hace más complicado. Usted podría intentar contemplar el enfoque en este artículo CodeProject:

Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above

+1

He intentado esto y CASI casi funciona. El problema es que hay algunas áreas de PNG que son simétricamente transparentes, y el método de la clave de color solo hace que los píxeles con una opacidad del 100% del valor del color de la clave de color sean transparentes. Si todo lo demás falla, mi alternativa será eliminar todas las regiones semitransparentes, pero preferiría evitar hacerlo. – adoss

+0

Puede intentar hacks más complicados como el del artículo que he vinculado, pero esencialmente tan pronto como esté buscando mezclar una imagen preexistente con el contenido del escritorio, las cosas se empiezan a complicar. –

+0

Mira la respuesta que publiqué. Me gusta ese código mucho mejor de lo que estaba viendo en este artículo de CodeProject. No solo eso, sino que creo que lo que encontré es la solución adecuada en lugar de un truco. ¡Gracias por la ayuda! – adoss

Cuestiones relacionadas