Рисование полупрозрачного дочернего окна с изображением на родительском окне

Мне нужно сделать анимацию птиц в WS_OVERLAPPED окно (как показано ниже). Анимация представлена ​​8 изображениями:

Анимация

Синий цвет на изображении (который RGB(0, 255, 255)) должен быть прозрачным (см. скриншот ниже).

Я хотел сделать это с помощью CreateWindowEx() (птица будет представлена ​​слоистым окном) с WS_EX_LAYERED аргумент. К сожалению птица должна быть WS_CHILD, смешивание WS_EX_LAYERED | WS_CHILD не является законным в Windows 7:

Windows 8: стиль WS_EX_LAYERED поддерживается для окон верхнего уровня и дочерних окон. Предыдущие версии Windows поддерживают WS_EX_LAYERED только для окон верхнего уровня.

Окончательный эффект должен выглядеть следующим образом (я уже нарисовал фон окна - единственная проблема - птица):

введите описание изображения здесь

Как я могу добиться этого эффекта? Как оживить птицу в родительском окне?
Если у вас есть идеи, как реализовать анимацию птиц с прозрачным фоновым цветом, пожалуйста, поделитесь.

2 ответа

Решение

Я наконец узнал, как это сделать. Это довольно сложно.

Полное описание решения доступно здесь - http://www.winprog.org/tutorial/transparency.html.
Для польских читателей здесь отличный перевод.

Простая идея вкратце:

Создание растровых изображений с прозрачными участками выглядит довольно просто и включает использование черно-белого маскирующего изображения в дополнение к цветному изображению, которое мы хотим видеть прозрачным.

Следующие условия должны быть выполнены для правильной работы эффекта: во-первых, цветное изображение должно быть черным во всех областях, которые мы хотим отобразить как прозрачные. И во-вторых, изображение маски должно быть белым в областях, которые мы хотим прозрачными, и черным в других местах. Цветные и маскирующие изображения отображаются как два самых левых изображения в примере изображения на этой странице.

Простое решение вкратце:

#define TRANSPARENCY_COLOR RGB(0, 255, 255)

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

Картина:

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;
}

Очистка:

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

Функция, которая отвечает за создание растровой маски:

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;
 }

Поскольку анимация выполняется даже тогда, когда нет взаимодействия с окном, нам понадобится таймер:

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;

Мы можем сделать недействительным все окно в каждом таймере, но было бы лучше перерисовать только необходимую часть. Мы также обновим текущий номер кадра здесь:

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;

Затем мы рисуем текущий кадр в WM_PAINT обработчик:

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;
Другие вопросы по тегам