سؤال

I need to make bird animation in WS_OVERLAPPED window (as shown below). Animation is represented by 8 images:

Animation

The blue color in the image (which is RGB(0, 255, 255)) has to be transparent (see screenshot below).

I wanted to do this using CreateWindowEx() (bird would be represented by layered window) with WS_EX_LAYERED argument. Unfortunately bird must be WS_CHILD. Mixing WS_EX_LAYERED | WS_CHILD is not legal in Windows 7:

Windows 8: The WS_EX_LAYERED style is supported for top-level windows and child windows. Previous Windows versions support WS_EX_LAYERED only for top-level windows.

Final effect should look like this (I've already painted window's background - the only problem is the bird):

enter image description here

How can I achieve this effect? How to animate bird in parent window?
If you have any idea how to implement bird animation with transparent background color please share.

هل كانت مفيدة؟

المحلول 2

I've finally find out how to do it. It's quite tricky.

The full description of the solution is available here - winprog.org/tutorial/transparency.html.
For polish readers here is great translation.

Simple idea in brief:

Giving bitmaps the appearance of having transparent sections is quite simple, and involves the use of a black and white Mask image in addition to the colour image that we want to look transparent.

The following conditions need to be met for the effect to work correctly: First off, the colour image must be black in all areas that we want to display as transparent. And second, the mask image must be white in the areas we want transparent, and black elsewhere. The colour and mask images are displayed as the two left most images in the example picture on this page.

Simple solution in brief:

#define TRANSPARENCY_COLOR RGB(0, 255, 255)

birdBmp = (HBITMAP) LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
hbmpMask = CreateBitmapMask(birdBmp, TRANSPARENCY_COLOR);

Painting:

case WM_PAINT:
{
    hdc = BeginPaint(hWnd, &ps);

    HDC birdMaskHdc = CreateCompatibleDC(hdc);

    BITMAP bmInfo;
    GetObject(birdBmp, sizeof(bmInfo), &bmInfo);

    HBITMAP hbmpOld = (HBITMAP) SelectObject(birdMaskHdc, hbmpMask);

    BitBlt(hdc, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, birdMaskHdc, 0, 0, SRCAND);
    SelectObject(birdMaskHdc, birdBmp);
    BitBlt(hdc, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, birdMaskHdc, 0, 0, SRCPAINT);

    SelectObject(birdMaskHdc, hbmpOld);
    DeleteDC(birdMaskHdc);

    EndPaint(hWnd, &ps);
    break;
}

Cleaning:

case WM_DESTROY:
{
    DeleteObject(hbmpMask);
    DeleteObject(birdBmp);
    PostQuitMessage(0);
    break;
}

Function which is responsible for creating bitmap mask:

HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
    HDC hdcMem, hdcMem2;
    HBITMAP hbmMask, hbmOld, hbmOld2;
    BITMAP bm;

    GetObject( hbmColour, sizeof( BITMAP ), & bm );
    hbmMask = CreateBitmap( bm.bmWidth, bm.bmHeight, 1, 1, NULL );

    hdcMem = CreateCompatibleDC( NULL );
    hdcMem2 = CreateCompatibleDC( NULL );

    hbmOld =( HBITMAP ) SelectObject( hdcMem, hbmColour );
    hbmOld2 =( HBITMAP ) SelectObject( hdcMem2, hbmMask );

    SetBkColor( hdcMem, crTransparent );

    BitBlt( hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY );
    BitBlt( hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT );

    SelectObject( hdcMem, hbmOld );
    SelectObject( hdcMem2, hbmOld2 );
    DeleteDC( hdcMem );
    DeleteDC( hdcMem2 );

    return hbmMask;
 }

نصائح أخرى

Since animation is done even when there's no interaction with the window, we'll need a timer:

case WM_CREATE:
    // load resources
    SetTimer(hwnd, 0, 250, NULL); // set timer to 250 ms
return 0;

...

case WM_DESTROY:
    KillTimer(hwnd, 0);
    // release the resources
return 0;

We can invalidate the whole window each timer tick, but it would be better to redraw only needed part. We'll also update the current frame number here:

case WM_TIMER:
    frame_number++;
    if (frame_number >= 8)
        frame_number = 0;

    RECT rc = { 30, 30, 80, 80 }; // a rectangle from (30,30) to (80,80)
    InvalidateRect(hwnd, &rc, FALSE);
return 0;

Then, we draw the current frame in the WM_PAINT handler:

case WM_PAINT:
    // draw the sky

    SelectObject(hDCMem, hBird);
    TransparentBlt(hDC, 30, 30, 50, 50, hDCMem, frame_number * 51, 0, 50, 50, RGB(0, 255, 255)); // 51 is 50 (side of a bird frame) + 1 (gap between the frames)

    // draw the rest
return 0;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top