Nano Hash - криптовалюты, майнинг, программирование

C++ CreateDIBitmap возвращает значение null, если перемещать окна

Я пишу демонстрационную программу для загрузки изображения из файла в формат OpenCV cv::Mat, затем конвертирую в растровое изображение и отображаю в окнах Win32. Это полный исходный код:

// Win32
#include <Windows.h>

// OpenCV
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#pragma comment(lib, "opencv_core310d.lib")
#pragma comment(lib, "opencv_imgcodecs310d.lib")

// Marco
#define WIN_CLASS_NAME  TEXT("DisplayTest")
#define WIN_NAME        TEXT("Display Test")
#define IMAGE_SRC       "./image.jpg"

// Global object
cv::Mat imgMat;
HWND hwndWindow;

// Convert cv::Mat data to bitmap
HBITMAP ConvertCVMatToBMP(cv::Mat frame)
{
    auto convertOpenCVBitDepthToBits = [](const INT32 value) {
        auto regular = 0u;

        switch (value) {
            case CV_8U:
            case CV_8S:
                regular = 8u;
                break;

            case CV_16U:
            case CV_16S:
                regular = 16u;
                break;

            case CV_32S:
            case CV_32F:
                regular = 32u;
                break;

            case CV_64F:
                regular = 64u;
                break;

            default:
                regular = 0u;
                break;
        }

        return regular;
    };

    auto imageSize = frame.size();

    if (imageSize.width && imageSize.height) {
        auto headerInfo = BITMAPINFOHEADER{};
        ZeroMemory(&headerInfo, sizeof(headerInfo));

        headerInfo.biSize = sizeof(headerInfo);
        headerInfo.biWidth = imageSize.width;
        headerInfo.biHeight = -(imageSize.height); // negative otherwise it will be upsidedown
        headerInfo.biPlanes = 1;// must be set to 1 as per documentation frame.channels();

        const auto bits = convertOpenCVBitDepthToBits(frame.depth());
        headerInfo.biBitCount = frame.channels() * bits;

        auto bitmapInfo = BITMAPINFO{};
        ZeroMemory(&bitmapInfo, sizeof(bitmapInfo));

        bitmapInfo.bmiHeader = headerInfo;
        bitmapInfo.bmiColors->rgbBlue = 0;
        bitmapInfo.bmiColors->rgbGreen = 0;
        bitmapInfo.bmiColors->rgbRed = 0;
        bitmapInfo.bmiColors->rgbReserved = 0;

        auto dc = GetDC(nullptr);
        assert(dc != nullptr && "Failure to get DC");

        auto bmp = CreateDIBitmap(dc,
                                  &headerInfo,
                                  CBM_INIT,
                                  frame.data,
                                  &bitmapInfo,
                                  DIB_RGB_COLORS);
        assert(bmp != nullptr && "Failure creating bitmap from captured frame");

        DeleteDC(dc);
        return bmp;
    }

    return nullptr;

}

// Attach image to windows
void attachImage()
{
    HBITMAP bitImage = (HBITMAP)ConvertCVMatToBMP(imgMat);

    // Display image
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwndWindow, &ps);

    HDC imageDC = CreateCompatibleDC(hdc);
    BITMAP bm;
    HBITMAP imageBmpOld = (HBITMAP)SelectObject(imageDC, (HGDIOBJ)bitImage);

    GetObject(bitImage, sizeof(bm), &bm);

    BitBlt(
        hdc,         // tell it we want to draw to the screen
        0, 0,            // as position 0,0 (upper-left corner)
        (int)bm.bmWidth,   // width of the rect to draw
        (int)bm.bmHeight,   // height of the rect
        imageDC,        // the DC to get the rect from (our image DC)
        0, 0,            // take it from position 0,0 in the image DC
        SRCCOPY         // tell it to do a pixel-by-pixel copy
    );
    SelectObject(imageDC, (HGDIOBJ)imageBmpOld);
    DeleteDC(imageDC);
    DeleteObject((HGDIOBJ)imageBmpOld);
    EndPaint(hwndWindow, &ps);
}

// WndProc callback
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
    switch (message) {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_PAINT:
            attachImage();
            break;

        default:
            return (DefWindowProc(hwnd, message, wparam, lparam));
    }
}

// Register class
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASS wc = { 0 };
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = WIN_CLASS_NAME;
    wc.style = CS_HREDRAW | CS_VREDRAW;

    return RegisterClass(&wc);
}

int main(int argc, char* argv[])
{
    // Register class
    char t[500];
    GetConsoleTitleA(t, 500);
    HWND hwndConsole = FindWindowA(NULL, t);
    HINSTANCE hInstance = (HINSTANCE)GetWindowLong(hwndConsole, GWL_HINSTANCE);
    MyRegisterClass(hInstance);

    // Init data
    imgMat = cv::imread(IMAGE_SRC);

    // Create Win32 windows
    hwndWindow = CreateWindow(WIN_CLASS_NAME, WIN_NAME, WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
                              CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                              NULL, NULL, hInstance, NULL);
    ShowWindow(hwndWindow, SW_SHOWNORMAL);
    UpdateWindow(hwndWindow);

    MSG msg;
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(109));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 0;
}

Этот код отлично работал у меня (Windows 10):
введите здесь описание изображения

Но если он работает в Windows 7, после быстрого перетаскивания окон так, чтобы они исчезли с экрана:
введите здесь описание изображения

И это результат после перемещения окна обратно на экран: введите здесь описание изображения

В этом случае assert(bmp != nullptr) упадет и программа внезапно завершится.
Этого не происходит в Windows 10!

Почему CreateDIBitmap возвращает null в этом случае???

15.09.2016

  • SelectObject(imageDC, (HGDIOBJ)imageBmpOld);, за которым следует DeleteDC(imageDC); и последний DeleteObject((HGDIOBJ)imageBmpOld); — довольно фатальная ошибка. Ознакомьтесь с контекстами устройств, чтобы получить хорошее представление. В вашем коде больше ошибок (даже много ошибок). Вам следует рассмотреть возможность прочтения книги, например книги Петцольда Programming Windows®, Fifth. Издание. 15.09.2016
  • Спасибо за помощь, @IInspectable. Я буду читать больше о программировании Win32 15.09.2016

Ответы:


1

SelectObject не создает дескриптор. В приведенной ниже функции вы не создаете imageBmpOld и не несете ответственности за его уничтожение. Но вы создали bitImage (он создан в ConvertCVMatToBMP), и вы должны уничтожить bitImage в конце.

// Attach image to windows
void attachImage()
{
    HBITMAP bitImage = (HBITMAP)ConvertCVMatToBMP(imgMat);
    ...
    HBITMAP imageBmpOld = (HBITMAP)SelectObject(imageDC, (HGDIOBJ)bitImage);
    ...
    SelectObject(imageDC, imageBmpOld);
    DeleteDC(imageDC);
    //DeleteObject((HGDIOBJ)imageBmpOld); <<== remove this line
    DeleteObject(bitImage); //add this
    EndPaint(hwndWindow, &ps);
}

Уборку для GetDC выполняет ReleaseDC, а не DeleteDC

HBITMAP ConvertCVMatToBMP(cv::Mat frame)
{
    ...    
    auto dc = GetDC(nullptr);
    assert(dc != nullptr && "Failure to get DC");
    auto bmp = CreateDIBitmap(dc, 
                    &headerInfo, CBM_INIT, frame.data, &bitmapInfo, DIB_RGB_COLORS);
    assert(bmp != nullptr && "Failure creating bitmap from captured frame");
    //DeleteDC(dc); <<== remove
    ReleaseDC(nullptr, dc); //<<== add
    ...
}

Ваша оконная процедура не всегда возвращает значение. Добавьте return 0; в конце.

Функция attachImage() не прикрепляет изображение к окну. Он только рисует изображение в окне. То, как вы его настроили, работает только в ответ на WM_PAINT, поэтому вам действительно следует переименовать его OnPaint()

Также FindWindow(NULL, title) ненадежен, потому что один и тот же заголовок может быть у нескольких окон. Вы должны использовать GetConsoleWindow, чтобы получить этот дескриптор окна:

int main()
{
    HWND hwndConsole = GetConsoleWindow();
    ...
}

Еще лучше, вы можете использовать точку входа WinMain, чтобы пропустить окно консоли. Самый простой способ - создать новый проект в вашей среде IDE, он должен позволить вам выбрать "проект win32" (а не "консольный проект win32").

//int main()
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
    ...
}
15.09.2016
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..