Нарисуйте HBITMAP в многоуровневом окне. Что случилось?

Всем привет и хорошего дня,

моя конечная цель - нарисовать файл PNG, включая альфа-канал, на экране - это означает, что не в собственное окно, а где-то на рабочем столе. Часть для загрузки PNG в HBITMAP теперь работает (проверено другим способом), но мне не удается отрисовать ее, включая альфу.

Насколько я слышал, лучший способ сделать это - использовать многослойные окна. Так что я много работал, чтобы переделать пару примеров и крошечных туториалов.

Следующий код компилируется без проблем и не выдает никаких сообщений (это означает, что функция showError("#") никогда не вызывается).

Однако на экране ничего не видно :/

Извините, что так длинно... Надеюсь, кому-то захочется посмотреть хотя бы бегло..

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);


int main(HINSTANCE hInstance)
{


    WNDCLASSEX WndClass;
    char sClassName[]  = "mainClass";
    WndClass.cbSize     = sizeof(WNDCLASSEX);
    WndClass.style      = NULL;
    WndClass.lpfnWndProc   = WndProc;//WndProc;
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hInstance  = hInstance;
    WndClass.hIcon      = NULL;
    WndClass.hCursor    = LoadCursor(NULL, IDC_ARROW);
    WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    WndClass.lpszMenuName  = NULL;
    WndClass.lpszClassName = sClassName;
    WndClass.hIconSm    = LoadIcon(NULL, IDI_APPLICATION);
    if (RegisterClassEx(&WndClass) == 0) showError("-1");





    HWND screen = CreateWindowEx(WS_EX_LAYERED,//WS_EX_LEFT
        "mainClass",
        "UpdateLayeredWind",
        WS_DISABLED | WS_VISIBLE,
        200,200,260,260,
        NULL /*eventuelly, GM window*/,
        NULL,
        hInstance,
        NULL);  


    if (screen == NULL) showError("0");




        HBITMAP img = LoadImageResource("D://ThreadDraw/ThreadDraw-test/ThreadDraw/test.png");
            if (img == NULL) showError("1");






    BLENDFUNCTION blend = {0};

    blend.AlphaFormat = AC_SRC_ALPHA;
    blend.SourceConstantAlpha = 155;

    POINT ptPos = {200,300};
    SIZE sizeWnd = {260,260};
    POINT ptPos2 = {200,300};


    ShowWindow(screen, SW_SHOW);



    while (1)
    {


        PAINTSTRUCT             ps;
        HDC                     hdc;
        BITMAP                  bitmap;
        HDC                     hdcMem;
        HGDIOBJ                 oldBitmap;

        hdc = BeginPaint(screen, &ps);

        hdcMem = CreateCompatibleDC(hdc);
        oldBitmap = SelectObject(hdcMem, img);

        GetObject(img, sizeof(bitmap), &bitmap);


        if (SetLayout(hdc,LAYOUT_RTL) == GDI_ERROR)
            showError("5");



            if (!BitBlt(hdc, 0, 0, 64, 64, hdcMem, 0, 0, SRCCOPY))
                showError("4");



            if (!UpdateLayeredWindow(screen,hdcMem,&ptPos,&sizeWnd,hdc,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA))//ULW_OPAQUE))
            showError("2");



        EndPaint(screen, &ps);

        SelectObject(hdcMem, oldBitmap);
        DeleteDC(hdcMem);


        Sleep(10);

    }



    return 0;
}



LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 
{
    switch(Message) 
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}

Кстати, если я использую ULW_OPAQUE вместо ULW_ALPHA в UpdateLayeredWindow, то появляется черное окно нужного размера, поэтому думаю, что проблема должна быть чем-то минимальным, связанным с функцией PAINTSTRUKT или BitBlt. Тем не менее, я пробовал много способов без каких-либо изменений .

Надеюсь, кто-то может помочь. Заранее большое спасибо!


person DragonGamer    schedule 27.09.2012    source источник


Ответы (1)


Это в основном неправильно. Ваш код должен:

  • Создайте многослойное окно с помощью CreateWindowEx.
  • Прикрепите к нему растровое изображение с помощью UpdateLayeredWindow.
  • Показать окно с ShowWindow. Windows позаботится о рисовании многослойного окна, поэтому вам не нужно обрабатывать WM_PAINT или вызывать BeginPaint.
  • Введите цикл сообщений.

И это все.

Если вы используете Visual Studio, создайте новый проект Win32, и он создаст для вас новый проект с циклом сообщений.

Обновить

Вот пример программы, которая создает прозрачное многоуровневое окно. Ему нужна функция для загрузки PNG в виде прозрачного растрового изображения. И у него нет проверки ошибок.

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    LPCTSTR szWindowClass = _T("TransparentClass");

    // Register class
    WNDCLASSEX wcex = {0};

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc    = DefWindowProc;
    wcex.hInstance      = hInstance;
    wcex.lpszClassName  = szWindowClass;

    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindowEx(WS_EX_LAYERED, szWindowClass, 0, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    int width;
    int height;
    HBITMAP hbmp = LoadPng(L"sample.png", &width, &height);

    HDC hdcScreen = GetDC(0);
    HDC hdc = CreateCompatibleDC(hdcScreen);
    ReleaseDC(0, hdcScreen);
    HBITMAP hbmpold = (HBITMAP)SelectObject(hdc, hbmp);

    POINT dcOffset = {0, 0};
    SIZE size = {width, height};
    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;
    UpdateLayeredWindow(hWnd, 0, 0, &size, hdc, &dcOffset, 0, &bf, ULW_ALPHA);
    SelectObject(hdc, hbmpold);
    DeleteDC(hdc);
    DeleteObject(hbmp);

    ShowWindow(hWnd, SW_SHOW);

    MSG msg;

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

    return (int)msg.wParam;
}

Еще одно обновление

Вот некоторый код для предварительного умножения значений красного, зеленого и синего на альфа-канал. Предполагается, что splash_image указывает на 32-битные данные ARGB размера width*height.

LPBYTE bits = (LPBYTE)splash_image;
int size = width * height;
for (int pixel = 0; pixel != size; ++pixel)
{
    bits[0] = bits[0] * bits[3] / 255;
    bits[1] = bits[1] * bits[3] / 255;
    bits[2] = bits[2] * bits[3] / 255;
    bits += 4;
}
person arx    schedule 27.09.2012
comment
Большое спасибо за ваше терпение! Боюсь, что это не очень помогает мне. Я действительно пробовал много способов с петлей и без нее, а также в разных порядках ... без какого-либо успеха. Особенно мне трудно достать прикрепляемую часть. Это правильный путь? HDC hdcMem = CreateCompatibleDC(GetDC(экран)); HGDIOBJ oldBitmap = SelectObject(hdcMem, img); UpdateLayeredWindow(экран,NULL,&ptPos,&sizeWnd,hdcMem,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA); Где я мог бы более подробно прочитать об этом? Кроме того, обратите внимание, что я пишу DLL, поэтому мне не нужен цикл сообщений. - person DragonGamer; 28.09.2012
comment
Согласен, если вы находитесь в DLL, вы можете обойтись без цикла сообщений, но вам определенно не нужен бесконечный цикл, который у вас есть. Я обновил свой ответ полной программой, за исключением кода для загрузки PNG. - person arx; 28.09.2012
comment
Большое спасибо еще раз! Думаю, теперь я понял, и это работает хорошо! Ну, почти. Есть неловкая проблема. Даже полностью непрозрачные объекты кажутся прозрачными (bf.SourceConstantAlpha имеет значение 255) или, скорее, смешиваются с фоном. На темном фоне выглядит как надо. На белом фоне цвета почти исчезают. В чем может быть проблема? Это не скрипт загрузки, потому что я также попробовал встроенную функцию, написав HBITMAP hbmp = (HBITMAP)LoadImage(...) для загрузки простого файла BMP - с тем же результатом --> изображение прозрачно на ярком фоне. ... - person DragonGamer; 29.09.2012
comment
GDI в основном не понимает прозрачности. Если вы загружаете непрозрачное растровое изображение с помощью LoadImage, это не обязательно правильно устанавливает прозрачность (непрозрачность), потому что GDI предполагает, что вы будете использовать изображение с функциями, которые не заботятся о прозрачности. Этот код работает нормально для меня, предполагая, что проблема заключается либо в вашем исходном PNG, либо в вашей функции для его загрузки. Обратите внимание, что для UpdateLayeredWindow требуется растровое изображение с предварительно умноженным альфа-каналом. - person arx; 29.09.2012
comment
Я уже думал об этой возможности, и теперь я попробовал rpemultiplied PNG_file (у меня есть инструмент для этого), и теперь цвета кажутся более правильными. Но только на черном фоне! Чем ярче фон, тем более прозрачным кажется изображение! Что может быть причиной этого? Не похоже, чтобы сам HBITMAP загружался неправильно... На вашем компьютере этого не происходит? - person DragonGamer; 29.09.2012
comment
Он отлично работает для меня. Единственное, что я могу придумать, что может привести к тому, что ваше растровое изображение будет вести себя непоследовательно на разных цветных фонах, - это неправильный предварительно умноженный альфа-канал. Это не то, что вы должны делать с PNG. Это то, что вам нужно сделать после загрузки PNG. - person arx; 30.09.2012
comment
О, хорошо. Мне приходилось иметь дело с rpemultiplication в среде языка сценариев, и мне было достаточно предварительно умножить поле перед их загрузкой. Однако не могли бы вы дать мне подсказку, как найти код для этого? По-видимому, это довольно необычная задача предварительно умножить hbitmap, потому что информации о нем не так много:/ - person DragonGamer; 01.10.2012
comment
Извините, случайно нажала ввод, прежде чем nad больше не мог редактировать ›‹ Я нашел только один бесплатный код: pastie.org/4892352 Но, к сожалению, это вызывает ошибки нарушения адреса (но не пользовательские или компиляционные ошибки):/ Может ли быть причиной загрузки HBITMAP проблема? Еще раз спасибо за ваше терпение! ._. - person DragonGamer; 01.10.2012
comment
Я добавил код для этого. Вы можете вставить его в загрузчик растровых изображений после инициализации splash_image. - person arx; 01.10.2012
comment
Медленно, но верно я собираюсь бросить эту чертову штуку... ›‹ Еще раз большое спасибо. Ваш код работал без ошибок, и теперь он, по-видимому, включает разные альфа-значения (результат СЕЙЧАС выглядит так, как должен, на черном фоне. Я сказал неправильно раньше). Тем не менее, этот эффект смешивания все еще существует! На белом фоне любое изображение совершенно невидимо... Должен ли я открыть новую тему для этой конкретной проблемы здесь? - person DragonGamer; 02.10.2012
comment
Вы пробовали другой PNG? например Возьмите любое случайное непрозрачное растровое изображение, используйте mspaint, чтобы сохранить его как PNG, и используйте его. Если это не говорит вам ничего полезного, то да, вы можете начать новый вопрос. - person arx; 02.10.2012
comment
Ооо спасибо за идею! Ну, теперь я полностью запутался, но счастлив xD Lol. Статистика: PNG, сохраненный в Paint Shop Pro 7 (да, очень старый, но полезный), вызывает этот неуклюжий эффект смешивания. PNG, сохраненный с помощью Paint (один из Win7), похоже, не загружается правильно. Изображение заполнено дерьмом, но эффекта смешивания нет! Затем я попробовал png, сохраненный из редактора спрайтов Game Maker (GM - это инструмент, для которого я делаю эту DLL, чтобы включить для него потоковое рисование). И тогда он работает идеально! O-o Другими словами: мин. три диф. способы сохранения png .. Ftw ›‹ По крайней мере, это решено. Спасибо! - person DragonGamer; 03.10.2012